5 stdjson "encoding/json"
9 log "github.com/sirupsen/logrus"
11 "github.com/vapor/blockchain/signers"
12 "github.com/vapor/blockchain/txbuilder"
13 "github.com/vapor/crypto/ed25519"
14 "github.com/vapor/crypto/ed25519/chainkd"
15 chainjson "github.com/vapor/encoding/json"
16 "github.com/vapor/errors"
17 "github.com/vapor/protocol/bc"
18 "github.com/vapor/protocol/bc/types"
19 "github.com/vapor/protocol/vm/vmutil"
22 // DecodeCrossInAction convert input data to action struct
23 func (r *Registry) DecodeCrossInAction(data []byte) (txbuilder.Action, error) {
24 a := &crossInAction{reg: r}
25 err := stdjson.Unmarshal(data, a)
29 type crossInAction struct {
32 SourceID string `json:"source_id"`
33 SourcePos uint64 `json:"source_pos"`
34 FedXPubs []chainkd.XPub `json:"fed_xpubs"`
35 Quorum int `json:"fed_quorum"`
36 AssetDefinition map[string]interface{} `json:"asset_definition"`
39 // TODO: also need to hard-code mapTx
40 func (a *crossInAction) Build(ctx context.Context, builder *txbuilder.TemplateBuilder) error {
42 if len(a.FedXPubs) == 0 {
43 missing = append(missing, "fed_xpubs")
46 missing = append(missing, "source_id")
48 if a.AssetId.IsZero() {
49 missing = append(missing, "asset_id")
52 missing = append(missing, "amount")
55 return txbuilder.MissingFieldsError(missing...)
58 sourceKey := []byte(fmt.Sprintf("SC:%v:%v", a.SourceID, a.SourcePos))
60 defer a.reg.assetMu.Unlock()
61 if existed := a.reg.db.Get(sourceKey); existed != nil {
62 return errors.New("mainchain output double spent")
67 if preAsset, _ := a.reg.GetAsset(a.AssetId.String()); preAsset != nil {
70 asset.RawDefinitionByte, err = serializeAssetDef(a.AssetDefinition)
75 if !chainjson.IsValidJSON(asset.RawDefinitionByte) {
76 return errors.New("asset definition is not in valid json format")
79 asset.DefinitionMap = a.AssetDefinition
81 asset.AssetID = *a.AssetId
82 extAlias := a.AssetId.String()
83 asset.Alias = &(extAlias)
84 a.reg.SaveExtAsset(asset, extAlias)
87 assetSigner, err := signers.Create("asset", a.FedXPubs, a.Quorum, 1, signers.BIP0032)
92 path := signers.GetBip0032Path(assetSigner, signers.AssetKeySpace)
93 derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
94 derivedPKs := chainkd.XPubKeys(derivedXPubs)
95 pegInScript, err := buildPegInScript(derivedPKs, assetSigner.Quorum)
101 if err := sourceID.UnmarshalText([]byte(a.SourceID)); err != nil {
102 return errors.New("invalid sourceID format")
105 // arguments will be set when materializeWitnesses
106 txin := types.NewCrossChainInput(nil, sourceID, *a.AssetId, a.Amount, a.SourcePos, pegInScript, asset.RawDefinitionByte)
107 log.Info("cross-chain input action built")
108 builder.RestrictMinTime(time.Now())
109 tplIn := &txbuilder.SigningInstruction{}
110 tplIn.AddRawWitnessKeys(assetSigner.XPubs, path, assetSigner.Quorum)
111 a.reg.db.Set(sourceKey, []byte("true"))
112 return builder.AddInput(txin, tplIn)
115 func (a *crossInAction) ActionType() string {
116 return "cross_chain_in"
119 func buildPegInScript(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, err error) {
120 controlProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
124 builder := vmutil.NewBuilder()
125 builder.AddRawBytes(controlProg)
126 prog, err := builder.Build()