OSDN Git Service

refactor: use derived xpubs for federation (#289)
[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/vapor/common"
11         cfg "github.com/vapor/config"
12         "github.com/vapor/consensus"
13         "github.com/vapor/encoding/json"
14         "github.com/vapor/protocol/bc"
15         "github.com/vapor/protocol/bc/types"
16         "github.com/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 }
155
156 func (a *crossOutAction) Build(ctx context.Context, b *TemplateBuilder) error {
157         var missing []string
158         if a.Address == "" {
159                 missing = append(missing, "address")
160         }
161         if a.AssetId.IsZero() {
162                 missing = append(missing, "asset_id")
163         }
164         if a.Amount == 0 {
165                 missing = append(missing, "amount")
166         }
167         if len(missing) > 0 {
168                 return MissingFieldsError(missing...)
169         }
170
171         address, err := common.DecodeAddress(a.Address, &consensus.BytomMainNetParams)
172         if err != nil {
173                 return err
174         }
175
176         redeemContract := address.ScriptAddress()
177         program := []byte{}
178         switch address.(type) {
179         case *common.AddressWitnessPubKeyHash:
180                 program, err = vmutil.P2WPKHProgram(redeemContract)
181         case *common.AddressWitnessScriptHash:
182                 program, err = vmutil.P2WSHProgram(redeemContract)
183         default:
184                 return errors.New("unsupport address type")
185         }
186         if err != nil {
187                 return err
188         }
189
190         out := types.NewCrossChainOutput(*a.AssetId, a.Amount, program)
191         return b.AddOutput(out)
192 }
193
194 func (a *crossOutAction) ActionType() string {
195         return "cross_chain_out"
196 }
197
198 // DecodeVoteOutputAction convert input data to action struct
199 func DecodeVoteOutputAction(data []byte) (Action, error) {
200         a := new(voteOutputAction)
201         err := stdjson.Unmarshal(data, a)
202         return a, err
203 }
204
205 type voteOutputAction struct {
206         bc.AssetAmount
207         Address string        `json:"address"`
208         Vote    json.HexBytes `json:"vote"`
209 }
210
211 func (a *voteOutputAction) Build(ctx context.Context, b *TemplateBuilder) error {
212         var missing []string
213         if a.Address == "" {
214                 missing = append(missing, "address")
215         }
216         if a.AssetId.IsZero() {
217                 missing = append(missing, "asset_id")
218         }
219         if a.Amount == 0 {
220                 missing = append(missing, "amount")
221         }
222         if len(a.Vote) == 0 {
223                 missing = append(missing, "vote")
224         }
225         if len(missing) > 0 {
226                 return MissingFieldsError(missing...)
227         }
228
229         address, err := common.DecodeAddress(a.Address, &consensus.ActiveNetParams)
230         if err != nil {
231                 return err
232         }
233
234         redeemContract := address.ScriptAddress()
235         program := []byte{}
236         switch address.(type) {
237         case *common.AddressWitnessPubKeyHash:
238                 program, err = vmutil.P2WPKHProgram(redeemContract)
239         case *common.AddressWitnessScriptHash:
240                 program, err = vmutil.P2WSHProgram(redeemContract)
241         default:
242                 return errors.New("unsupport address type")
243         }
244         if err != nil {
245                 return err
246         }
247
248         out := types.NewVoteOutput(*a.AssetId, a.Amount, program, a.Vote)
249         return b.AddOutput(out)
250 }
251
252 func (a *voteOutputAction) ActionType() string {
253         return "vote_output"
254 }
255
256 // DecodeCrossInAction convert input data to action struct
257 func DecodeCrossInAction(data []byte) (Action, error) {
258         a := new(crossInAction)
259         err := stdjson.Unmarshal(data, a)
260         return a, err
261 }
262
263 type crossInAction struct {
264         bc.AssetAmount
265         SourceID          bc.Hash       `json:"source_id"`
266         SourcePos         uint64        `json:"source_pos"`
267         VMVersion         uint64        `json:"vm_version"`
268         RawDefinitionByte json.HexBytes `json:"raw_definition_byte"`
269         IssuanceProgram   json.HexBytes `json:"issuance_program"`
270 }
271
272 func (a *crossInAction) Build(ctx context.Context, builder *TemplateBuilder) error {
273         var missing []string
274         if a.SourceID.IsZero() {
275                 missing = append(missing, "source_id")
276         }
277         if a.AssetId.IsZero() {
278                 missing = append(missing, "asset_id")
279         }
280         if a.Amount == 0 {
281                 missing = append(missing, "amount")
282         }
283
284         if len(missing) > 0 {
285                 return MissingFieldsError(missing...)
286         }
287
288         if err := a.checkAssetID(); err != nil {
289                 return err
290         }
291
292         // arguments will be set when materializeWitnesses
293         txin := types.NewCrossChainInput(nil, a.SourceID, *a.AssetId, a.Amount, a.SourcePos, a.VMVersion, a.RawDefinitionByte, a.IssuanceProgram)
294         tplIn := &SigningInstruction{}
295         fed := cfg.CommonConfig.Federation
296         tplIn.AddRawWitnessKeys(fed.Xpubs, cfg.FedAddressPath, fed.Quorum)
297         tplIn.AddDataWitness(cfg.FederationPMultiSigScript(cfg.CommonConfig))
298         return builder.AddInput(txin, tplIn)
299 }
300
301 func (a *crossInAction) ActionType() string {
302         return "cross_chain_in"
303 }
304
305 func (c *crossInAction) checkAssetID() error {
306         defHash := bc.NewHash(sha3.Sum256(c.RawDefinitionByte))
307         assetID := bc.ComputeAssetID(c.IssuanceProgram, c.VMVersion, &defHash)
308
309         if *c.AssetId != *consensus.BTMAssetID && assetID != *c.AssetAmount.AssetId {
310                 return errors.New("incorrect asset_idincorrect asset_id")
311         }
312
313         return nil
314 }