OSDN Git Service

Merge pull request #341 from Bytom/dev
[bytom/bytom.git] / protocol / validation / validation_test.go
1 package validation
2
3 import (
4         "fmt"
5         "math"
6         "testing"
7
8         "github.com/davecgh/go-spew/spew"
9         "github.com/golang/protobuf/proto"
10
11         "github.com/bytom/consensus"
12         "github.com/bytom/crypto/sha3pool"
13         "github.com/bytom/errors"
14         "github.com/bytom/protocol/bc"
15         "github.com/bytom/protocol/bc/legacy"
16         "github.com/bytom/protocol/vm"
17         "github.com/bytom/protocol/vm/vmutil"
18         "github.com/bytom/testutil"
19 )
20
21 const dirPath = "pseudohsm/testdata/pseudo"
22
23 func init() {
24         spew.Config.DisableMethods = true
25 }
26
27 func TestGasStatus(t *testing.T) {
28         cases := []struct {
29                 input  *gasState
30                 output *gasState
31                 f      func(*gasState) error
32                 err    error
33         }{
34                 {
35                         input: &gasState{
36                                 gasLeft:  10000,
37                                 gasUsed:  0,
38                                 BTMValue: 0,
39                         },
40                         output: &gasState{
41                                 gasLeft:  10000 / GasRate,
42                                 gasUsed:  0,
43                                 BTMValue: 10000,
44                         },
45                         f: func(input *gasState) error {
46                                 return input.setGas(10000)
47                         },
48                         err: nil,
49                 },
50                 {
51                         input: &gasState{
52                                 gasLeft:  10000,
53                                 gasUsed:  0,
54                                 BTMValue: 0,
55                         },
56                         output: &gasState{
57                                 gasLeft:  10000,
58                                 gasUsed:  0,
59                                 BTMValue: 0,
60                         },
61                         f: func(input *gasState) error {
62                                 return input.setGas(-10000)
63                         },
64                         err: errGasCalculate,
65                 },
66                 {
67                         input: &gasState{
68                                 gasLeft:  defaultGasLimit,
69                                 gasUsed:  0,
70                                 BTMValue: 0,
71                         },
72                         output: &gasState{
73                                 gasLeft:  defaultGasLimit,
74                                 gasUsed:  0,
75                                 BTMValue: 80000000000,
76                         },
77                         f: func(input *gasState) error {
78                                 return input.setGas(80000000000)
79                         },
80                         err: nil,
81                 },
82                 {
83                         input: &gasState{
84                                 gasLeft:  10000,
85                                 gasUsed:  0,
86                                 BTMValue: 0,
87                         },
88                         output: &gasState{
89                                 gasLeft:  10000,
90                                 gasUsed:  0,
91                                 BTMValue: 0,
92                         },
93                         f: func(input *gasState) error {
94                                 return input.updateUsage(-1)
95                         },
96                         err: errGasCalculate,
97                 },
98                 {
99                         input: &gasState{
100                                 gasLeft:  10000,
101                                 gasUsed:  0,
102                                 BTMValue: 0,
103                         },
104                         output: &gasState{
105                                 gasLeft:  9999,
106                                 gasUsed:  1,
107                                 BTMValue: 0,
108                         },
109                         f: func(input *gasState) error {
110                                 return input.updateUsage(9999)
111                         },
112                         err: nil,
113                 },
114         }
115
116         for _, c := range cases {
117                 err := c.f(c.input)
118
119                 if err != c.err {
120                         t.Errorf("got error %s, want %s", err, c.err)
121                 } else if *c.input != *c.output {
122                         t.Errorf("got gasStatus %s, want %s;", c.input, c.output)
123                 }
124         }
125 }
126
127 func TestTxValidation(t *testing.T) {
128         var (
129                 tx      *bc.Tx
130                 vs      *validationState
131                 fixture *txFixture
132
133                 // the mux from tx, pulled out for convenience
134                 mux *bc.Mux
135         )
136
137         cases := []struct {
138                 desc string // description of the test case
139                 f    func() // function to adjust tx, vs, and/or mux
140                 err  error  // expected error
141         }{
142                 {
143                         desc: "base case",
144                 },
145                 {
146                         desc: "failing mux program",
147                         f: func() {
148                                 mux.Program.Code = []byte{byte(vm.OP_FALSE)}
149                         },
150                         err: vm.ErrFalseVMResult,
151                 },
152                 {
153                         desc: "unbalanced mux amounts",
154                         f: func() {
155                                 mux.Sources[0].Value.Amount++
156                                 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
157                                 iss.WitnessDestination.Value.Amount++
158                         },
159                         err: errUnbalanced,
160                 },
161                 {
162                         desc: "overflowing mux source amounts",
163                         f: func() {
164                                 mux.Sources[0].Value.Amount = math.MaxInt64
165                                 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
166                                 iss.WitnessDestination.Value.Amount = math.MaxInt64
167                         },
168                         err: errOverflow,
169                 },
170                 {
171                         desc: "underflowing mux destination amounts",
172                         f: func() {
173                                 mux.WitnessDestinations[0].Value.Amount = math.MaxInt64
174                                 out := tx.Entries[*mux.WitnessDestinations[0].Ref].(*bc.Output)
175                                 out.Source.Value.Amount = math.MaxInt64
176                                 mux.WitnessDestinations[1].Value.Amount = math.MaxInt64
177                                 out = tx.Entries[*mux.WitnessDestinations[1].Ref].(*bc.Output)
178                                 out.Source.Value.Amount = math.MaxInt64
179                         },
180                         err: errOverflow,
181                 },
182                 {
183                         desc: "unbalanced mux assets",
184                         f: func() {
185                                 mux.Sources[1].Value.AssetId = newAssetID(255)
186                                 sp := tx.Entries[*mux.Sources[1].Ref].(*bc.Spend)
187                                 sp.WitnessDestination.Value.AssetId = newAssetID(255)
188                         },
189                         err: errUnbalanced,
190                 },
191                 {
192                         desc: "nonempty mux exthash",
193                         f: func() {
194                                 mux.ExtHash = newHash(1)
195                         },
196                         err: errNonemptyExtHash,
197                 },
198                 {
199                         desc: "nonempty mux exthash, but that's OK",
200                         f: func() {
201                                 tx.Version = 2
202                                 mux.ExtHash = newHash(1)
203                         },
204                 },
205                 {
206                         desc: "failing nonce program",
207                         f: func() {
208                                 iss := txIssuance(t, tx, 0)
209                                 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
210                                 nonce.Program.Code = []byte{byte(vm.OP_FALSE)}
211                         },
212                         err: vm.ErrFalseVMResult,
213                 },
214                 {
215                         desc: "nonce exthash nonempty",
216                         f: func() {
217                                 iss := txIssuance(t, tx, 0)
218                                 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
219                                 nonce.ExtHash = newHash(1)
220                         },
221                         err: errNonemptyExtHash,
222                 },
223                 {
224                         desc: "nonce exthash nonempty, but that's OK",
225                         f: func() {
226                                 tx.Version = 2
227                                 iss := txIssuance(t, tx, 0)
228                                 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
229                                 nonce.ExtHash = newHash(1)
230                         },
231                 },
232                 {
233                         desc: "mismatched output source / mux dest position",
234                         f: func() {
235                                 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
236                         },
237                         err: errMismatchedPosition,
238                 },
239                 {
240                         desc: "mismatched output source and mux dest",
241                         f: func() {
242                                 // For this test, it's necessary to construct a mostly
243                                 // identical second transaction in order to get a similar but
244                                 // not equal output entry for the mux to falsely point
245                                 // to. That entry must be added to the first tx's Entries map.
246                                 fixture.txOutputs[0].ReferenceData = []byte{1}
247                                 fixture2 := sample(t, fixture)
248                                 tx2 := legacy.NewTx(*fixture2.tx).Tx
249                                 out2ID := tx2.ResultIds[0]
250                                 out2 := tx2.Entries[*out2ID].(*bc.Output)
251                                 tx.Entries[*out2ID] = out2
252                                 mux.WitnessDestinations[0].Ref = out2ID
253                         },
254                         err: errMismatchedReference,
255                 },
256                 {
257                         desc: "invalid mux destination position",
258                         f: func() {
259                                 mux.WitnessDestinations[0].Position = 1
260                         },
261                         err: errPosition,
262                 },
263                 {
264                         desc: "mismatched mux dest value / output source value",
265                         f: func() {
266                                 outID := tx.ResultIds[0]
267                                 out := tx.Entries[*outID].(*bc.Output)
268                                 mux.WitnessDestinations[0].Value = &bc.AssetAmount{
269                                         AssetId: out.Source.Value.AssetId,
270                                         Amount:  out.Source.Value.Amount + 1,
271                                 }
272                                 mux.Sources[0].Value.Amount++ // the mux must still balance
273                         },
274                         err: errMismatchedValue,
275                 },
276                 {
277                         desc: "output exthash nonempty",
278                         f: func() {
279                                 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
280                         },
281                         err: errNonemptyExtHash,
282                 },
283                 {
284                         desc: "output exthash nonempty, but that's OK",
285                         f: func() {
286                                 tx.Version = 2
287                                 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
288                         },
289                 },
290                 {
291                         desc: "empty tx results",
292                         f: func() {
293                                 tx.ResultIds = nil
294                         },
295                         err: errEmptyResults,
296                 },
297                 {
298                         desc: "empty tx results, but that's OK",
299                         f: func() {
300                                 tx.Version = 2
301                                 tx.ResultIds = nil
302                         },
303                 },
304                 {
305                         desc: "tx header exthash nonempty",
306                         f: func() {
307                                 tx.ExtHash = newHash(1)
308                         },
309                         err: errNonemptyExtHash,
310                 },
311                 {
312                         desc: "tx header exthash nonempty, but that's OK",
313                         f: func() {
314                                 tx.Version = 2
315                                 tx.ExtHash = newHash(1)
316                         },
317                 },
318                 {
319                         desc: "issuance program failure",
320                         f: func() {
321                                 iss := txIssuance(t, tx, 0)
322                                 iss.WitnessArguments[0] = []byte{}
323                         },
324                         err: vm.ErrFalseVMResult,
325                 },
326                 {
327                         desc: "issuance exthash nonempty",
328                         f: func() {
329                                 iss := txIssuance(t, tx, 0)
330                                 iss.ExtHash = newHash(1)
331                         },
332                         err: errNonemptyExtHash,
333                 },
334                 {
335                         desc: "issuance exthash nonempty, but that's OK",
336                         f: func() {
337                                 tx.Version = 2
338                                 iss := txIssuance(t, tx, 0)
339                                 iss.ExtHash = newHash(1)
340                         },
341                 },
342                 {
343                         desc: "spend control program failure",
344                         f: func() {
345                                 spend := txSpend(t, tx, 1)
346                                 spend.WitnessArguments[0] = []byte{}
347                         },
348                         err: vm.ErrFalseVMResult,
349                 },
350                 {
351                         desc: "mismatched spent source/witness value",
352                         f: func() {
353                                 spend := txSpend(t, tx, 1)
354                                 spentOutput := tx.Entries[*spend.SpentOutputId].(*bc.Output)
355                                 spentOutput.Source.Value = &bc.AssetAmount{
356                                         AssetId: spend.WitnessDestination.Value.AssetId,
357                                         Amount:  spend.WitnessDestination.Value.Amount + 1,
358                                 }
359                         },
360                         err: errMismatchedValue,
361                 },
362                 {
363                         desc: "spend exthash nonempty",
364                         f: func() {
365                                 spend := txSpend(t, tx, 1)
366                                 spend.ExtHash = newHash(1)
367                         },
368                         err: errNonemptyExtHash,
369                 },
370                 {
371                         desc: "spend exthash nonempty, but that's OK",
372                         f: func() {
373                                 tx.Version = 2
374                                 spend := txSpend(t, tx, 1)
375                                 spend.ExtHash = newHash(1)
376                         },
377                 },
378         }
379
380         for _, c := range cases {
381                 gasVaild := 0
382                 t.Run(c.desc, func(t *testing.T) {
383                         fixture = sample(t, nil)
384                         tx = legacy.NewTx(*fixture.tx).Tx
385                         vs = &validationState{
386                                 block:   mockBlock(),
387                                 tx:      tx,
388                                 entryID: tx.ID,
389                                 gas: &gasState{
390                                         gasLeft: int64(80000),
391                                         gasUsed: 0,
392                                 },
393                                 cache:    make(map[bc.Hash]error),
394                                 gasVaild: &gasVaild,
395                         }
396                         out := tx.Entries[*tx.ResultIds[0]].(*bc.Output)
397                         muxID := out.Source.Ref
398                         mux = tx.Entries[*muxID].(*bc.Mux)
399
400                         if c.f != nil {
401                                 c.f()
402                         }
403                         err := checkValid(vs, tx.TxHeader)
404
405                         if rootErr(err) != c.err {
406                                 t.Errorf("got error %s, want %s; validationState is:\n%s", err, c.err, spew.Sdump(vs))
407                         }
408                 })
409         }
410 }
411
412 func TestValidateBlock(t *testing.T) {
413         cases := []struct {
414                 block *bc.Block
415                 err   error
416         }{
417                 {
418                         block: &bc.Block{
419                                 BlockHeader: &bc.BlockHeader{
420                                         Height: 0,
421                                         Bits:   2305843009230471167,
422                                 },
423                                 Transactions: []*bc.Tx{mockCoinbaseTx(1470000000000000000)},
424                         },
425                         err: nil,
426                 },
427                 {
428                         block: &bc.Block{
429                                 BlockHeader: &bc.BlockHeader{
430                                         Height: 0,
431                                         Bits:   2305843009230471167,
432                                 },
433                                 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
434                         },
435                         err: errWrongCoinbaseTransaction,
436                 },
437                 {
438                         block: &bc.Block{
439                                 BlockHeader: &bc.BlockHeader{
440                                         Height:         0,
441                                         Bits:           2305843009230471167,
442                                         SerializedSize: 88888888,
443                                 },
444                                 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
445                         },
446                         err: errWrongBlockSize,
447                 },
448         }
449
450         for _, c := range cases {
451                 txRoot, err := bc.MerkleRoot(c.block.Transactions)
452                 if err != nil {
453                         t.Errorf("computing transaction merkle root", err)
454                         continue
455                 }
456                 c.block.BlockHeader.TransactionStatus = bc.NewTransactionStatus()
457                 c.block.TransactionsRoot = &txRoot
458
459                 if err = ValidateBlock(c.block, nil); rootErr(err) != c.err {
460                         t.Errorf("got error %s, want %s", err, c.err)
461                 }
462         }
463 }
464
465 func TestCoinbase(t *testing.T) {
466         CbTx := mockCoinbaseTx(5000000000)
467         errCbTx := legacy.MapTx(&legacy.TxData{
468                 Inputs: []*legacy.TxInput{
469                         legacy.NewCoinbaseInput(nil, nil),
470                 },
471                 Outputs: []*legacy.TxOutput{
472                         legacy.NewTxOutput(bc.AssetID{
473                                 V0: uint64(18446744073709551611),
474                                 V1: uint64(18446744073709551615),
475                                 V2: uint64(18446744073709551615),
476                                 V3: uint64(18446744073709551615),
477                         }, 800000000000, []byte{1}, nil),
478                 },
479         })
480         cases := []struct {
481                 block    *bc.Block
482                 tx       *bc.Tx
483                 gasVaild bool
484                 err      error
485         }{
486                 {
487                         block: &bc.Block{
488                                 BlockHeader: &bc.BlockHeader{
489                                         Height: 666,
490                                 },
491                                 Transactions: []*bc.Tx{errCbTx},
492                         },
493                         tx:       CbTx,
494                         gasVaild: true,
495                         err:      errWrongCoinbaseTransaction,
496                 },
497                 {
498                         block: &bc.Block{
499                                 BlockHeader: &bc.BlockHeader{
500                                         Height: 666,
501                                 },
502                                 Transactions: []*bc.Tx{CbTx},
503                         },
504                         tx:       CbTx,
505                         gasVaild: true,
506                         err:      nil,
507                 },
508                 {
509                         block: &bc.Block{
510                                 BlockHeader: &bc.BlockHeader{
511                                         Height: 666,
512                                 },
513                                 Transactions: []*bc.Tx{errCbTx},
514                         },
515                         tx:       errCbTx,
516                         gasVaild: true,
517                         err:      errWrongCoinbaseAsset,
518                 },
519         }
520
521         for _, c := range cases {
522                 _, gasVaild, err := ValidateTx(c.tx, c.block)
523
524                 if rootErr(err) != c.err {
525                         t.Errorf("got error %s, want %s", err, c.err)
526                 }
527                 if c.gasVaild != gasVaild {
528                         t.Errorf("got gasVaild %s, want %s", gasVaild, c.gasVaild)
529                 }
530         }
531 }
532
533 func TestBlockHeaderValid(t *testing.T) {
534         base := bc.NewBlockHeader(1, 1, &bc.Hash{}, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, nil, 0, 0)
535         baseBytes, _ := proto.Marshal(base)
536
537         var bh bc.BlockHeader
538
539         cases := []struct {
540                 f   func()
541                 err error
542         }{
543                 {},
544                 {
545                         f: func() {
546                                 bh.Version = 2
547                         },
548                 },
549         }
550
551         for i, c := range cases {
552                 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
553                         proto.Unmarshal(baseBytes, &bh)
554                         if c.f != nil {
555                                 c.f()
556                         }
557                 })
558         }
559 }
560
561 // A txFixture is returned by sample (below) to produce a sample
562 // transaction, which takes a separate, optional _input_ txFixture to
563 // affect the transaction that's built. The components of the
564 // transaction are the fields of txFixture.
565 type txFixture struct {
566         initialBlockID bc.Hash
567         issuanceProg   bc.Program
568         issuanceArgs   [][]byte
569         assetDef       []byte
570         assetID        bc.AssetID
571         txVersion      uint64
572         txInputs       []*legacy.TxInput
573         txOutputs      []*legacy.TxOutput
574         txRefData      []byte
575         tx             *legacy.TxData
576 }
577
578 // Produces a sample transaction in a txFixture object (see above). A
579 // separate input txFixture can be used to alter the transaction
580 // that's created.
581 //
582 // The output of this function can be used as the input to a
583 // subsequent call to make iterative refinements to a test object.
584 //
585 // The default transaction produced is valid and has three inputs:
586 //  - an issuance of 10 units
587 //  - a spend of 20 units
588 //  - a spend of 40 units
589 // and two outputs, one of 25 units and one of 45 units.
590 // All amounts are denominated in the same asset.
591 //
592 // The issuance program for the asset requires two numbers as
593 // arguments that add up to 5. The prevout control programs require
594 // two numbers each, adding to 9 and 13, respectively.
595 //
596 // The min and max times for the transaction are now +/- one minute.
597 func sample(tb testing.TB, in *txFixture) *txFixture {
598         var result txFixture
599         if in != nil {
600                 result = *in
601         }
602
603         if result.initialBlockID.IsZero() {
604                 result.initialBlockID = *newHash(1)
605         }
606         if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
607                 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
608                 if err != nil {
609                         tb.Fatal(err)
610                 }
611                 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
612         }
613         if len(result.issuanceArgs) == 0 {
614                 result.issuanceArgs = [][]byte{[]byte{2}, []byte{3}}
615         }
616         if len(result.assetDef) == 0 {
617                 result.assetDef = []byte{2}
618         }
619         if result.assetID.IsZero() {
620                 refdatahash := hashData(result.assetDef)
621                 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, &result.initialBlockID, result.issuanceProg.VmVersion, &refdatahash)
622         }
623
624         if result.txVersion == 0 {
625                 result.txVersion = 1
626         }
627         if len(result.txInputs) == 0 {
628                 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
629                 if err != nil {
630                         tb.Fatal(err)
631                 }
632                 args1 := [][]byte{[]byte{4}, []byte{5}}
633
634                 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
635                 if err != nil {
636                         tb.Fatal(err)
637                 }
638                 args2 := [][]byte{[]byte{6}, []byte{7}}
639
640                 result.txInputs = []*legacy.TxInput{
641                         legacy.NewIssuanceInput([]byte{3}, 10, []byte{4}, result.initialBlockID, result.issuanceProg.Code, result.issuanceArgs, result.assetDef),
642                         legacy.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1, *newHash(6), []byte{7}),
643                         legacy.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2, *newHash(9), []byte{10}),
644                 }
645         }
646
647         result.txInputs = append(result.txInputs, mockGasTxInput())
648
649         if len(result.txOutputs) == 0 {
650                 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
651                 if err != nil {
652                         tb.Fatal(err)
653                 }
654                 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
655                 if err != nil {
656                         tb.Fatal(err)
657                 }
658
659                 result.txOutputs = []*legacy.TxOutput{
660                         legacy.NewTxOutput(result.assetID, 25, cp1, []byte{11}),
661                         legacy.NewTxOutput(result.assetID, 45, cp2, []byte{12}),
662                 }
663         }
664         if len(result.txRefData) == 0 {
665                 result.txRefData = []byte{13}
666         }
667
668         result.tx = &legacy.TxData{
669                 Version:       result.txVersion,
670                 Inputs:        result.txInputs,
671                 Outputs:       result.txOutputs,
672                 ReferenceData: result.txRefData,
673         }
674
675         return &result
676 }
677
678 func mockBlock() *bc.Block {
679         return &bc.Block{
680                 BlockHeader: &bc.BlockHeader{
681                         Height: 666,
682                 },
683         }
684 }
685
686 func mockCoinbaseTx(amount uint64) *bc.Tx {
687         cp, _ := vmutil.DefaultCoinbaseProgram()
688         return legacy.MapTx(&legacy.TxData{
689                 Inputs: []*legacy.TxInput{
690                         legacy.NewCoinbaseInput(nil, nil),
691                 },
692                 Outputs: []*legacy.TxOutput{
693                         legacy.NewTxOutput(*consensus.BTMAssetID, amount, cp, nil),
694                 },
695         })
696 }
697
698 func mockGasTxInput() *legacy.TxInput {
699         cp, _ := vmutil.DefaultCoinbaseProgram()
700         return legacy.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp, *newHash(9), []byte{})
701 }
702
703 // Like errors.Root, but also unwraps vm.Error objects.
704 func rootErr(e error) error {
705         for {
706                 e = errors.Root(e)
707                 if e2, ok := e.(vm.Error); ok {
708                         e = e2.Err
709                         continue
710                 }
711                 return e
712         }
713 }
714
715 func hashData(data []byte) bc.Hash {
716         var b32 [32]byte
717         sha3pool.Sum256(b32[:], data)
718         return bc.NewHash(b32)
719 }
720
721 func newHash(n byte) *bc.Hash {
722         h := bc.NewHash([32]byte{n})
723         return &h
724 }
725
726 func newAssetID(n byte) *bc.AssetID {
727         a := bc.NewAssetID([32]byte{n})
728         return &a
729 }
730
731 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
732         id := tx.InputIDs[index]
733         res, err := tx.Issuance(id)
734         if err != nil {
735                 t.Fatal(err)
736         }
737         return res
738 }
739
740 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
741         id := tx.InputIDs[index]
742         res, err := tx.Spend(id)
743         if err != nil {
744                 t.Fatal(err)
745         }
746         return res
747 }