OSDN Git Service

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