OSDN Git Service

Mov (#518)
[bytom/vapor.git] / blockchain / txbuilder / actions.go
1 package txbuilder
2
3 import (
4         "context"
5         stdjson "encoding/json"
6         "errors"
7
8         "golang.org/x/crypto/sha3"
9
10         "github.com/bytom/vapor/common"
11         cfg "github.com/bytom/vapor/config"
12         "github.com/bytom/vapor/consensus"
13         "github.com/bytom/vapor/encoding/json"
14         "github.com/bytom/vapor/protocol/bc"
15         "github.com/bytom/vapor/protocol/bc/types"
16         "github.com/bytom/vapor/protocol/vm/vmutil"
17 )
18
19 // DecodeControlAddressAction convert input data to action struct
20 func DecodeControlAddressAction(data []byte) (Action, error) {
21         a := new(controlAddressAction)
22         err := stdjson.Unmarshal(data, a)
23         return a, err
24 }
25
26 type controlAddressAction struct {
27         bc.AssetAmount
28         Address string `json:"address"`
29 }
30
31 func (a *controlAddressAction) Build(ctx context.Context, b *TemplateBuilder) error {
32         var missing []string
33         if a.Address == "" {
34                 missing = append(missing, "address")
35         }
36         if a.AssetId.IsZero() {
37                 missing = append(missing, "asset_id")
38         }
39         if a.Amount == 0 {
40                 missing = append(missing, "amount")
41         }
42         if len(missing) > 0 {
43                 return MissingFieldsError(missing...)
44         }
45
46         address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
47         if err != nil {
48                 return err
49         }
50
51         redeemContract := address.ScriptAddress()
52         program := []byte{}
53         switch address.(type) {
54         case *common.AddressWitnessPubKeyHash:
55                 program, err = vmutil.P2WPKHProgram(redeemContract)
56         case *common.AddressWitnessScriptHash:
57                 program, err = vmutil.P2WSHProgram(redeemContract)
58         default:
59                 return errors.New("unsupport address type")
60         }
61         if err != nil {
62                 return err
63         }
64
65         out := types.NewIntraChainOutput(*a.AssetId, a.Amount, program)
66         return b.AddOutput(out)
67 }
68
69 func (a *controlAddressAction) ActionType() string {
70         return "control_address"
71 }
72
73 // DecodeControlProgramAction convert input data to action struct
74 func DecodeControlProgramAction(data []byte) (Action, error) {
75         a := new(controlProgramAction)
76         err := stdjson.Unmarshal(data, a)
77         return a, err
78 }
79
80 type controlProgramAction struct {
81         bc.AssetAmount
82         Program json.HexBytes `json:"control_program"`
83 }
84
85 func (a *controlProgramAction) Build(ctx context.Context, b *TemplateBuilder) error {
86         var missing []string
87         if len(a.Program) == 0 {
88                 missing = append(missing, "control_program")
89         }
90         if a.AssetId.IsZero() {
91                 missing = append(missing, "asset_id")
92         }
93         if a.Amount == 0 {
94                 missing = append(missing, "amount")
95         }
96         if len(missing) > 0 {
97                 return MissingFieldsError(missing...)
98         }
99
100         out := types.NewIntraChainOutput(*a.AssetId, a.Amount, a.Program)
101         return b.AddOutput(out)
102 }
103
104 func (a *controlProgramAction) ActionType() string {
105         return "control_program"
106 }
107
108 // DecodeRetireAction convert input data to action struct
109 func DecodeRetireAction(data []byte) (Action, error) {
110         a := new(retireAction)
111         err := stdjson.Unmarshal(data, a)
112         return a, err
113 }
114
115 type retireAction struct {
116         bc.AssetAmount
117         Arbitrary json.HexBytes `json:"arbitrary"`
118 }
119
120 func (a *retireAction) Build(ctx context.Context, b *TemplateBuilder) error {
121         var missing []string
122         if a.AssetId.IsZero() {
123                 missing = append(missing, "asset_id")
124         }
125         if a.Amount == 0 {
126                 missing = append(missing, "amount")
127         }
128         if len(missing) > 0 {
129                 return MissingFieldsError(missing...)
130         }
131
132         program, err := vmutil.RetireProgram(a.Arbitrary)
133         if err != nil {
134                 return err
135         }
136         out := types.NewIntraChainOutput(*a.AssetId, a.Amount, program)
137         return b.AddOutput(out)
138 }
139
140 func (a *retireAction) ActionType() string {
141         return "retire"
142 }
143
144 // DecodeCrossOutAction convert input data to action struct
145 func DecodeCrossOutAction(data []byte) (Action, error) {
146         a := new(crossOutAction)
147         err := stdjson.Unmarshal(data, a)
148         return a, err
149 }
150
151 type crossOutAction struct {
152         bc.AssetAmount
153         Address string        `json:"address"`
154         Program json.HexBytes `json:"control_program"`
155 }
156
157 func (a *crossOutAction) Build(ctx context.Context, b *TemplateBuilder) error {
158         var missing []string
159         if a.Address == "" && len(a.Program) == 0 {
160                 missing = append(missing, "address or program")
161         }
162         if a.AssetId.IsZero() {
163                 missing = append(missing, "asset_id")
164         }
165         if a.Amount == 0 {
166                 missing = append(missing, "amount")
167         }
168         if len(missing) > 0 {
169                 return MissingFieldsError(missing...)
170         }
171
172         program := a.Program
173         if a.Address != "" {
174                 address, err := common.DecodeAddress(a.Address, consensus.BytomMainNetParams(&consensus.ActiveNetParams))
175                 if err != nil {
176                         return err
177                 }
178
179                 redeemContract := address.ScriptAddress()
180                 switch address.(type) {
181                 case *common.AddressWitnessPubKeyHash:
182                         program, err = vmutil.P2WPKHProgram(redeemContract)
183                 case *common.AddressWitnessScriptHash:
184                         program, err = vmutil.P2WSHProgram(redeemContract)
185                 default:
186                         return errors.New("unsupport address type")
187                 }
188                 if err != nil {
189                         return err
190                 }
191         }
192
193         out := types.NewCrossChainOutput(*a.AssetId, a.Amount, program)
194         return b.AddOutput(out)
195 }
196
197 func (a *crossOutAction) ActionType() string {
198         return "cross_chain_out"
199 }
200
201 // DecodeVoteOutputAction convert input data to action struct
202 func DecodeVoteOutputAction(data []byte) (Action, error) {
203         a := new(voteOutputAction)
204         err := stdjson.Unmarshal(data, a)
205         return a, err
206 }
207
208 type voteOutputAction struct {
209         bc.AssetAmount
210         Address string        `json:"address"`
211         Vote    json.HexBytes `json:"vote"`
212 }
213
214 func (a *voteOutputAction) Build(ctx context.Context, b *TemplateBuilder) error {
215         var missing []string
216         if a.Address == "" {
217                 missing = append(missing, "address")
218         }
219         if a.AssetId.IsZero() {
220                 missing = append(missing, "asset_id")
221         }
222         if a.Amount == 0 {
223                 missing = append(missing, "amount")
224         }
225         if len(a.Vote) == 0 {
226                 missing = append(missing, "vote")
227         }
228         if len(missing) > 0 {
229                 return MissingFieldsError(missing...)
230         }
231
232         address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
233         if err != nil {
234                 return err
235         }
236
237         redeemContract := address.ScriptAddress()
238         program := []byte{}
239         switch address.(type) {
240         case *common.AddressWitnessPubKeyHash:
241                 program, err = vmutil.P2WPKHProgram(redeemContract)
242         case *common.AddressWitnessScriptHash:
243                 program, err = vmutil.P2WSHProgram(redeemContract)
244         default:
245                 return errors.New("unsupport address type")
246         }
247         if err != nil {
248                 return err
249         }
250
251         out := types.NewVoteOutput(*a.AssetId, a.Amount, program, a.Vote)
252         return b.AddOutput(out)
253 }
254
255 func (a *voteOutputAction) ActionType() string {
256         return "vote_output"
257 }
258
259 // DecodeCrossInAction convert input data to action struct
260 func DecodeCrossInAction(data []byte) (Action, error) {
261         a := new(crossInAction)
262         err := stdjson.Unmarshal(data, a)
263         return a, err
264 }
265
266 type crossInAction struct {
267         bc.AssetAmount
268         SourceID          bc.Hash       `json:"source_id"`
269         SourcePos         uint64        `json:"source_pos"`
270         VMVersion         uint64        `json:"vm_version"`
271         RawDefinitionByte json.HexBytes `json:"raw_definition_byte"`
272         IssuanceProgram   json.HexBytes `json:"issuance_program"`
273 }
274
275 func (c *crossInAction) Build(ctx context.Context, builder *TemplateBuilder) error {
276         var missing []string
277         if c.SourceID.IsZero() {
278                 missing = append(missing, "source_id")
279         }
280         if c.AssetId.IsZero() {
281                 missing = append(missing, "asset_id")
282         }
283         if c.Amount == 0 {
284                 missing = append(missing, "amount")
285         }
286
287         if len(missing) > 0 {
288                 return MissingFieldsError(missing...)
289         }
290
291         if err := c.checkAssetID(); err != nil {
292                 return err
293         }
294
295         // arguments will be set when materializeWitnesses
296         txin := types.NewCrossChainInput(nil, c.SourceID, *c.AssetId, c.Amount, c.SourcePos, c.VMVersion, c.RawDefinitionByte, c.IssuanceProgram)
297         tplIn := &SigningInstruction{}
298         fed := cfg.CommonConfig.Federation
299
300         if !common.IsOpenFederationIssueAsset(c.RawDefinitionByte) {
301                 tplIn.AddRawWitnessKeys(fed.Xpubs, cfg.FedAddressPath, fed.Quorum)
302                 tplIn.AddDataWitness(cfg.FederationPMultiSigScript(cfg.CommonConfig))
303         }
304
305         return builder.AddInput(txin, tplIn)
306 }
307
308 func (c *crossInAction) ActionType() string {
309         return "cross_chain_in"
310 }
311
312 func (c *crossInAction) checkAssetID() error {
313         defHash := bc.NewHash(sha3.Sum256(c.RawDefinitionByte))
314         assetID := bc.ComputeAssetID(c.IssuanceProgram, c.VMVersion, &defHash)
315
316         if *c.AssetId != *consensus.BTMAssetID && assetID != *c.AssetAmount.AssetId {
317                 return errors.New("incorrect asset_idincorrect asset_id")
318         }
319
320         return nil
321 }