OSDN Git Service

3cd4bfcdcfd967e849dd23611ff7afed96c6daa6
[bytom/vapor.git] / asset / builder.go
1 package asset
2
3 import (
4         "context"
5         stdjson "encoding/json"
6         "fmt"
7         "time"
8
9         log "github.com/sirupsen/logrus"
10
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"
20 )
21
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)
26         return a, err
27 }
28
29 type crossInAction struct {
30         reg *Registry
31         bc.AssetAmount
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"`
37 }
38
39 // TODO: also need to hard-code mapTx
40 func (a *crossInAction) Build(ctx context.Context, builder *txbuilder.TemplateBuilder) error {
41         var missing []string
42         if len(a.FedXPubs) == 0 {
43                 missing = append(missing, "fed_xpubs")
44         }
45         if a.SourceID == "" {
46                 missing = append(missing, "source_id")
47         }
48         if a.AssetId.IsZero() {
49                 missing = append(missing, "asset_id")
50         }
51         if a.Amount == 0 {
52                 missing = append(missing, "amount")
53         }
54         if len(missing) > 0 {
55                 return txbuilder.MissingFieldsError(missing...)
56         }
57
58         sourceKey := []byte(fmt.Sprintf("SC:%v:%v", a.SourceID, a.SourcePos))
59         a.reg.assetMu.Lock()
60         defer a.reg.assetMu.Unlock()
61         if existed := a.reg.db.Get(sourceKey); existed != nil {
62                 return errors.New("mainchain output double spent")
63         }
64
65         var err error
66         asset := &Asset{}
67         if preAsset, _ := a.reg.GetAsset(a.AssetId.String()); preAsset != nil {
68                 asset = preAsset
69         } else {
70                 asset.RawDefinitionByte, err = serializeAssetDef(a.AssetDefinition)
71                 if err != nil {
72                         return ErrSerializing
73                 }
74
75                 if !chainjson.IsValidJSON(asset.RawDefinitionByte) {
76                         return errors.New("asset definition is not in valid json format")
77                 }
78
79                 asset.DefinitionMap = a.AssetDefinition
80                 asset.VMVersion = 1
81                 asset.AssetID = *a.AssetId
82                 extAlias := a.AssetId.String()
83                 asset.Alias = &(extAlias)
84                 a.reg.SaveExtAsset(asset, extAlias)
85         }
86
87         assetSigner, err := signers.Create("asset", a.FedXPubs, a.Quorum, 1, signers.BIP0032)
88         if err != nil {
89                 return err
90         }
91
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)
96         if err != nil {
97                 return err
98         }
99
100         var sourceID bc.Hash
101         if err := sourceID.UnmarshalText([]byte(a.SourceID)); err != nil {
102                 return errors.New("invalid sourceID format")
103         }
104
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)
113 }
114
115 func (a *crossInAction) ActionType() string {
116         return "cross_chain_in"
117 }
118
119 func buildPegInScript(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, err error) {
120         controlProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
121         if err != nil {
122                 return nil, err
123         }
124         builder := vmutil.NewBuilder()
125         builder.AddRawBytes(controlProg)
126         prog, err := builder.Build()
127         return prog, err
128 }