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) <= 1 {
43 missing = append(missing, "fed_xpubs")
46 missing = append(missing, "fed_quorum")
49 missing = append(missing, "source_id")
51 if a.AssetId.IsZero() {
52 missing = append(missing, "asset_id")
55 missing = append(missing, "amount")
58 return txbuilder.MissingFieldsError(missing...)
61 sourceKey := []byte(fmt.Sprintf("SC:%v:%v", a.SourceID, a.SourcePos))
63 defer a.reg.assetMu.Unlock()
64 if existed := a.reg.db.Get(sourceKey); existed != nil {
65 return errors.New("mainchain output double spent")
70 if preAsset, _ := a.reg.GetAsset(a.AssetId.String()); preAsset != nil {
73 asset.RawDefinitionByte, err = serializeAssetDef(a.AssetDefinition)
78 if !chainjson.IsValidJSON(asset.RawDefinitionByte) {
79 return errors.New("asset definition is not in valid json format")
82 asset.DefinitionMap = a.AssetDefinition
84 asset.AssetID = *a.AssetId
85 extAlias := a.AssetId.String()
86 asset.Alias = &(extAlias)
87 a.reg.SaveExtAsset(asset, extAlias)
90 assetSigner, err := signers.Create("asset", a.FedXPubs, a.Quorum, 1, signers.BIP0032)
95 path := signers.GetBip0032Path(assetSigner, signers.AssetKeySpace)
96 derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
97 derivedPKs := chainkd.XPubKeys(derivedXPubs)
98 pegInScript, err := buildPegInScript(derivedPKs, assetSigner.Quorum)
104 if err := sourceID.UnmarshalText([]byte(a.SourceID)); err != nil {
105 return errors.New("invalid sourceID format")
108 // arguments will be set when materializeWitnesses
109 txin := types.NewCrossChainInput(nil, sourceID, *a.AssetId, a.Amount, a.SourcePos, pegInScript, asset.RawDefinitionByte)
110 log.Info("cross-chain input action built")
111 builder.RestrictMinTime(time.Now())
112 tplIn := &txbuilder.SigningInstruction{}
113 tplIn.AddRawWitnessKeys(assetSigner.XPubs, path, assetSigner.Quorum)
114 a.reg.db.Set(sourceKey, []byte("true"))
115 return builder.AddInput(txin, tplIn)
118 func (a *crossInAction) ActionType() string {
119 return "cross_chain_in"
122 func buildPegInScript(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, err error) {
123 controlProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
127 builder := vmutil.NewBuilder()
128 builder.AddRawBytes(controlProg)
129 prog, err := builder.Build()