OSDN Git Service

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