OSDN Git Service

wwww
[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) <= 1 {
43                 missing = append(missing, "fed_xpubs")
44         }
45         if a.Quorum == 0 {
46                 missing = append(missing, "fed_quorum")
47         }
48         if a.SourceID == "" {
49                 missing = append(missing, "source_id")
50         }
51         if a.AssetId.IsZero() {
52                 missing = append(missing, "asset_id")
53         }
54         if a.Amount == 0 {
55                 missing = append(missing, "amount")
56         }
57         if len(missing) > 0 {
58                 return txbuilder.MissingFieldsError(missing...)
59         }
60
61         sourceKey := []byte(fmt.Sprintf("SC:%v:%v", a.SourceID, a.SourcePos))
62         a.reg.assetMu.Lock()
63         defer a.reg.assetMu.Unlock()
64         if existed := a.reg.db.Get(sourceKey); existed != nil {
65                 return errors.New("mainchain output double spent")
66         }
67
68         var err error
69         asset := &Asset{}
70         if preAsset, _ := a.reg.GetAsset(a.AssetId.String()); preAsset != nil {
71                 asset = preAsset
72         } else {
73                 asset.RawDefinitionByte, err = serializeAssetDef(a.AssetDefinition)
74                 if err != nil {
75                         return ErrSerializing
76                 }
77
78                 if !chainjson.IsValidJSON(asset.RawDefinitionByte) {
79                         return errors.New("asset definition is not in valid json format")
80                 }
81
82                 asset.DefinitionMap = a.AssetDefinition
83                 asset.VMVersion = 1
84                 asset.AssetID = *a.AssetId
85                 extAlias := a.AssetId.String()
86                 asset.Alias = &(extAlias)
87                 a.reg.SaveExtAsset(asset, extAlias)
88         }
89
90         assetSigner, err := signers.Create("asset", a.FedXPubs, a.Quorum, 1, signers.BIP0032)
91         if err != nil {
92                 return err
93         }
94
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)
99         if err != nil {
100                 return err
101         }
102
103         var sourceID bc.Hash
104         if err := sourceID.UnmarshalText([]byte(a.SourceID)); err != nil {
105                 return errors.New("invalid sourceID format")
106         }
107
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)
116 }
117
118 func (a *crossInAction) ActionType() string {
119         return "cross_chain_in"
120 }
121
122 func buildPegInScript(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, err error) {
123         controlProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
124         if err != nil {
125                 return nil, err
126         }
127         builder := vmutil.NewBuilder()
128         builder.AddRawBytes(controlProg)
129         prog, err := builder.Build()
130         return prog, err
131 }