OSDN Git Service

consensus should be able to change (#300)
[bytom/vapor.git] / blockchain / txbuilder / txbuilder_test.go
1 package txbuilder
2
3 import (
4         "context"
5         "encoding/hex"
6         "encoding/json"
7         "math"
8         "testing"
9         "time"
10
11         "github.com/davecgh/go-spew/spew"
12
13         "github.com/vapor/common"
14         "github.com/vapor/consensus"
15         "github.com/vapor/crypto"
16         "github.com/vapor/crypto/ed25519/chainkd"
17         chainjson "github.com/vapor/encoding/json"
18         "github.com/vapor/errors"
19         "github.com/vapor/protocol/bc"
20         "github.com/vapor/protocol/bc/types"
21         "github.com/vapor/protocol/vm/vmutil"
22         "github.com/vapor/testutil"
23 )
24
25 type testAction bc.AssetAmount
26
27 func (t testAction) Build(ctx context.Context, b *TemplateBuilder) error {
28         in := types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), *t.AssetId, t.Amount, 0, nil)
29         tplIn := &SigningInstruction{}
30
31         err := b.AddInput(in, tplIn)
32         if err != nil {
33                 return err
34         }
35         return b.AddOutput(types.NewIntraChainOutput(*t.AssetId, t.Amount, []byte("change")))
36 }
37
38 func (t testAction) ActionType() string {
39         return "test-action"
40 }
41
42 func newControlProgramAction(assetAmt bc.AssetAmount, script []byte) *controlProgramAction {
43         return &controlProgramAction{
44                 AssetAmount: assetAmt,
45                 Program:     script,
46         }
47 }
48
49 func TestBuildIntra(t *testing.T) {
50         ctx := context.Background()
51
52         assetID1 := bc.NewAssetID([32]byte{1})
53         assetID2 := bc.NewAssetID([32]byte{2})
54
55         actions := []Action{
56                 newControlProgramAction(bc.AssetAmount{AssetId: &assetID2, Amount: 6}, []byte("dest")),
57                 testAction(bc.AssetAmount{AssetId: &assetID1, Amount: 5}),
58         }
59         expiryTime := time.Now().Add(time.Minute)
60         got, err := Build(ctx, nil, actions, expiryTime, 0)
61         if err != nil {
62                 t.Fatal(err)
63         }
64
65         want := &Template{
66                 Transaction: types.NewTx(types.TxData{
67                         Version: 1,
68                         Inputs: []*types.TxInput{
69                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), assetID1, 5, 0, nil),
70                         },
71                         Outputs: []*types.TxOutput{
72                                 types.NewIntraChainOutput(assetID2, 6, []byte("dest")),
73                                 types.NewIntraChainOutput(assetID1, 5, []byte("change")),
74                         },
75                 }),
76                 SigningInstructions: []*SigningInstruction{{
77                         WitnessComponents: []witnessComponent{},
78                 }},
79         }
80
81         if !testutil.DeepEqual(got.Transaction.TxData, want.Transaction.TxData) {
82                 t.Errorf("got tx:\n%s\nwant tx:\n%s", spew.Sdump(got.Transaction.TxData), spew.Sdump(want.Transaction.TxData))
83         }
84
85         if !testutil.DeepEqual(got.SigningInstructions, want.SigningInstructions) {
86                 t.Errorf("got signing instructions:\n\t%#v\nwant signing instructions:\n\t%#v", got.SigningInstructions, want.SigningInstructions)
87         }
88 }
89
90 func newCrossOutAction(assetAmt bc.AssetAmount, redeemContract []byte) *crossOutAction {
91         address, err := common.NewAddressWitnessPubKeyHash(redeemContract, consensus.BytomMainNetParams(&consensus.ActiveNetParams))
92         if err != nil {
93                 panic(err)
94         }
95
96         return &crossOutAction{
97                 AssetAmount: assetAmt,
98                 Address:     address.String(),
99         }
100 }
101
102 func TestBuildCrossOut(t *testing.T) {
103         ctx := context.Background()
104
105         assetID1 := bc.NewAssetID([32]byte{1})
106         assetID2 := bc.NewAssetID([32]byte{2})
107
108         redeemContract := make([]byte, 20)
109         controlProgram := append([]byte{0x00, byte(len(redeemContract))}, redeemContract...)
110
111         actions := []Action{
112                 newCrossOutAction(bc.AssetAmount{AssetId: &assetID2, Amount: 6}, redeemContract),
113                 testAction(bc.AssetAmount{AssetId: &assetID1, Amount: 5}),
114         }
115         expiryTime := time.Now().Add(time.Minute)
116         got, err := Build(ctx, nil, actions, expiryTime, 0)
117         if err != nil {
118                 t.Fatal(err)
119         }
120
121         want := &Template{
122                 Transaction: types.NewTx(types.TxData{
123                         Version: 1,
124                         Inputs: []*types.TxInput{
125                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), assetID1, 5, 0, nil),
126                         },
127                         Outputs: []*types.TxOutput{
128                                 types.NewCrossChainOutput(assetID2, 6, controlProgram),
129                                 types.NewIntraChainOutput(assetID1, 5, []byte("change")),
130                         },
131                 }),
132                 SigningInstructions: []*SigningInstruction{{
133                         WitnessComponents: []witnessComponent{},
134                 }},
135         }
136
137         if !testutil.DeepEqual(got.Transaction.TxData, want.Transaction.TxData) {
138                 t.Errorf("got tx:\n%s\nwant tx:\n%s", spew.Sdump(got.Transaction.TxData), spew.Sdump(want.Transaction.TxData))
139         }
140
141         if !testutil.DeepEqual(got.SigningInstructions, want.SigningInstructions) {
142                 t.Errorf("got signing instructions:\n\t%#v\nwant signing instructions:\n\t%#v", got.SigningInstructions, want.SigningInstructions)
143         }
144 }
145
146 func mustDecodeHex(str string) []byte {
147         data, err := hex.DecodeString(str)
148         if err != nil {
149                 panic(err)
150         }
151         return data
152 }
153
154 func TestCheckBlankCheck(t *testing.T) {
155         cases := []struct {
156                 tx   *types.TxData
157                 want error
158         }{{
159                 tx: &types.TxData{
160                         Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
161                 },
162                 want: ErrBlankCheck,
163         }, {
164                 tx: &types.TxData{
165                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
166                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 3, nil)},
167                 },
168                 want: ErrBlankCheck,
169         }, {
170                 tx: &types.TxData{
171                         Inputs: []*types.TxInput{
172                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
173                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.NewAssetID([32]byte{1}), 5, 0, nil),
174                         },
175                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 5, nil)},
176                 },
177                 want: ErrBlankCheck,
178         }, {
179                 tx: &types.TxData{
180                         Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
181                         Outputs: []*types.TxOutput{
182                                 types.NewIntraChainOutput(bc.AssetID{}, math.MaxInt64, nil),
183                                 types.NewIntraChainOutput(bc.AssetID{}, 7, nil),
184                         },
185                 },
186                 want: ErrBadAmount,
187         }, {
188                 tx: &types.TxData{
189                         Inputs: []*types.TxInput{
190                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
191                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, math.MaxInt64, 0, nil),
192                         },
193                 },
194                 want: ErrBadAmount,
195         }, {
196                 tx: &types.TxData{
197                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
198                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 5, nil)},
199                 },
200                 want: nil,
201         }, {
202                 tx: &types.TxData{
203                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 5, nil)},
204                 },
205                 want: nil,
206         }, {
207                 tx: &types.TxData{
208                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
209                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.NewAssetID([32]byte{1}), 5, nil)},
210                 },
211                 want: nil,
212         }}
213
214         for _, c := range cases {
215                 got := checkBlankCheck(c.tx)
216                 if errors.Root(got) != c.want {
217                         t.Errorf("checkUnsafe(%+v) err = %v want %v", c.tx, errors.Root(got), c.want)
218                 }
219         }
220 }
221
222 func TestCreateTxByUtxo(t *testing.T) {
223         xprv, xpub, err := chainkd.NewXKeys(nil)
224         if err != nil {
225                 t.Fatal(err)
226         }
227
228         pub := xpub.PublicKey()
229         pubHash := crypto.Ripemd160(pub)
230         program, err := vmutil.P2WPKHProgram([]byte(pubHash))
231         if err != nil {
232                 t.Fatal(err)
233         }
234
235         address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
236         if err != nil {
237                 t.Fatal(err)
238         }
239
240         muxID := testutil.MustDecodeHash("1e673900965623ec3305cead5a78dfb68a34599f8bc078460f3f202256c3dfa6")
241         utxo := struct {
242                 SourceID       bc.Hash
243                 AssetID        bc.AssetID
244                 Amount         uint64
245                 SourcePos      uint64
246                 ControlProgram []byte
247                 Address        string
248         }{
249                 SourceID:       muxID,
250                 AssetID:        *consensus.BTMAssetID,
251                 Amount:         20000000000,
252                 SourcePos:      1,
253                 ControlProgram: program,
254                 Address:        address.EncodeAddress(),
255         }
256
257         recvProg := mustDecodeHex("00145056532ecd3621c9ce8adde5505c058610b287cf")
258         tx := types.NewTx(types.TxData{
259                 Version: 1,
260                 Inputs: []*types.TxInput{
261                         types.NewSpendInput(nil, utxo.SourceID, utxo.AssetID, utxo.Amount, utxo.SourcePos, utxo.ControlProgram),
262                 },
263                 Outputs: []*types.TxOutput{
264                         types.NewIntraChainOutput(*consensus.BTMAssetID, 10000000000, recvProg),
265                 },
266         })
267
268         tpl := &Template{
269                 Transaction:     tx,
270                 AllowAdditional: false,
271         }
272
273         h := tpl.Hash(0).Byte32()
274         sig := xprv.Sign(h[:])
275         data := []byte(pub)
276
277         // Test with more signatures than required, in correct order
278         tpl.SigningInstructions = []*SigningInstruction{{
279                 WitnessComponents: []witnessComponent{
280                         &RawTxSigWitness{
281                                 Quorum: 1,
282                                 Sigs:   []chainjson.HexBytes{sig},
283                         },
284                         DataWitness(data),
285                 },
286         }}
287
288         if err = materializeWitnesses(tpl); err != nil {
289                 t.Fatal(err)
290         }
291
292         if !testutil.DeepEqual(tx, tpl.Transaction) {
293                 t.Errorf("tx:%v result is equal to want:%v", tx, tpl.Transaction)
294         }
295 }
296
297 func TestAddContractArgs(t *testing.T) {
298         hexXpub, err := hex.DecodeString("ba76bb52574b3f40315f2c01f1818a9072ced56e9d4b68acbef56a4d0077d08e5e34837963e4cdc54eb251aa34aad01e6ae48b140f6a2743fbb0a0abd9cf8aac")
299         if err != nil {
300                 t.Fatal(err)
301         }
302
303         var xpub chainkd.XPub
304         copy(xpub[:], hexXpub)
305
306         rawTxSig := RawTxSigArgument{RootXPub: xpub, Path: []chainjson.HexBytes{{1, 1, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}}}
307         rawTxSigMsg, err := json.Marshal(rawTxSig)
308         if err != nil {
309                 t.Fatal(err)
310         }
311
312         value, err := hex.DecodeString("7468697320697320612074657374")
313         if err != nil {
314                 t.Fatal(err)
315         }
316         data := DataArgument{value}
317         dataMsg, err := json.Marshal(data)
318         if err != nil {
319                 t.Fatal(err)
320         }
321
322         strMsg, err := json.Marshal(StrArgument{"this is a test string"})
323         if err != nil {
324                 t.Fatal(err)
325         }
326
327         integerMsg, err := json.Marshal(IntegerArgument{100})
328         if err != nil {
329                 t.Fatal(err)
330         }
331
332         boolMsg, err := json.Marshal(BoolArgument{true})
333         if err != nil {
334                 t.Fatal(err)
335         }
336
337         cases := []struct {
338                 arguments  []ContractArgument
339                 wantResult error
340         }{
341                 {
342                         arguments: []ContractArgument{
343                                 {
344                                         Type:    "raw_tx_signature",
345                                         RawData: rawTxSigMsg,
346                                 },
347                         },
348                         wantResult: nil,
349                 },
350                 {
351                         arguments: []ContractArgument{
352                                 {
353                                         Type:    "data",
354                                         RawData: dataMsg,
355                                 },
356                         },
357                         wantResult: nil,
358                 },
359                 {
360                         arguments: []ContractArgument{
361                                 {
362                                         Type:    "string",
363                                         RawData: strMsg,
364                                 },
365                         },
366                         wantResult: nil,
367                 },
368                 {
369                         arguments: []ContractArgument{
370                                 {
371                                         Type:    "integer",
372                                         RawData: integerMsg,
373                                 },
374                         },
375                         wantResult: nil,
376                 },
377                 {
378                         arguments: []ContractArgument{
379                                 {
380                                         Type:    "boolean",
381                                         RawData: boolMsg,
382                                 },
383                         },
384                         wantResult: nil,
385                 },
386                 {
387                         arguments: []ContractArgument{
388                                 {
389                                         Type:    "raw_tx_signature",
390                                         RawData: rawTxSigMsg,
391                                 },
392                                 {
393                                         Type:    "data",
394                                         RawData: dataMsg,
395                                 },
396                         },
397                         wantResult: nil,
398                 },
399                 {
400                         arguments: []ContractArgument{
401                                 {
402                                         Type:    "data",
403                                         RawData: dataMsg,
404                                 },
405                                 {
406                                         Type:    "raw_tx_signature",
407                                         RawData: rawTxSigMsg,
408                                 },
409                         },
410                         wantResult: nil,
411                 },
412                 {
413                         arguments: []ContractArgument{
414                                 {
415                                         Type:    "raw_tx_signature",
416                                         RawData: rawTxSigMsg,
417                                 },
418                                 {
419                                         Type:    "data",
420                                         RawData: dataMsg,
421                                 },
422                                 {
423                                         Type:    "string",
424                                         RawData: strMsg,
425                                 },
426                                 {
427                                         Type:    "integer",
428                                         RawData: integerMsg,
429                                 },
430                                 {
431                                         Type:    "boolean",
432                                         RawData: boolMsg,
433                                 },
434                         },
435                         wantResult: nil,
436                 },
437                 {
438                         arguments: []ContractArgument{
439                                 {
440                                         Type:    "data",
441                                         RawData: dataMsg,
442                                 },
443                                 {
444                                         Type:    "err_data",
445                                         RawData: rawTxSigMsg,
446                                 },
447                         },
448                         wantResult: ErrBadContractArgType,
449                 },
450         }
451
452         sigInst := &SigningInstruction{}
453         for _, c := range cases {
454                 err := AddContractArgs(sigInst, c.arguments)
455                 if err != c.wantResult {
456                         t.Fatalf("got result=%v, want result=%v", err, c.wantResult)
457                 }
458         }
459 }