4 "github.com/bytom/consensus"
10 "github.com/bytom/crypto/sha3pool"
11 "github.com/bytom/errors"
12 "github.com/bytom/protocol/bc"
13 "github.com/bytom/protocol/bc/legacy"
14 "github.com/bytom/protocol/vm"
15 "github.com/bytom/testutil"
17 "github.com/davecgh/go-spew/spew"
18 "github.com/golang/protobuf/proto"
22 spew.Config.DisableMethods = true
25 func TestGasStatus(t *testing.T) {
29 f func(*gasState) error
39 gasLeft: 10000 / gasRate,
43 f: func(input *gasState) error {
44 return input.setGas(10000)
59 f: func(input *gasState) error {
60 return input.setGas(-10000)
66 gasLeft: defaultGasLimit,
71 gasLeft: defaultGasLimit,
73 BTMValue: 80000000000,
75 f: func(input *gasState) error {
76 return input.setGas(80000000000)
91 f: func(input *gasState) error {
92 return input.updateUsage(-1)
107 f: func(input *gasState) error {
108 return input.updateUsage(9999)
114 for _, c := range cases {
118 t.Errorf("got error %s, want %s", err, c.err)
119 } else if *c.input != *c.output {
120 t.Errorf("got gasStatus %s, want %s;", c.input, c.output)
125 func TestTxValidation(t *testing.T) {
131 // the mux from tx, pulled out for convenience
136 desc string // description of the test case
137 f func() // function to adjust tx, vs, and/or mux
138 err error // expected error
144 desc: "failing mux program",
146 mux.Program.Code = []byte{byte(vm.OP_FALSE)}
148 err: vm.ErrFalseVMResult,
151 desc: "unbalanced mux amounts",
153 mux.Sources[0].Value.Amount++
154 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
155 iss.WitnessDestination.Value.Amount++
160 desc: "overflowing mux source amounts",
162 mux.Sources[0].Value.Amount = math.MaxInt64
163 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
164 iss.WitnessDestination.Value.Amount = math.MaxInt64
169 desc: "underflowing mux destination amounts",
171 mux.WitnessDestinations[0].Value.Amount = math.MaxInt64
172 out := tx.Entries[*mux.WitnessDestinations[0].Ref].(*bc.Output)
173 out.Source.Value.Amount = math.MaxInt64
174 mux.WitnessDestinations[1].Value.Amount = math.MaxInt64
175 out = tx.Entries[*mux.WitnessDestinations[1].Ref].(*bc.Output)
176 out.Source.Value.Amount = math.MaxInt64
181 desc: "unbalanced mux assets",
183 mux.Sources[1].Value.AssetId = newAssetID(255)
184 sp := tx.Entries[*mux.Sources[1].Ref].(*bc.Spend)
185 sp.WitnessDestination.Value.AssetId = newAssetID(255)
190 desc: "nonempty mux exthash",
192 mux.ExtHash = newHash(1)
194 err: errNonemptyExtHash,
197 desc: "nonempty mux exthash, but that's OK",
200 mux.ExtHash = newHash(1)
204 desc: "failing nonce program",
206 iss := txIssuance(t, tx, 0)
207 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
208 nonce.Program.Code = []byte{byte(vm.OP_FALSE)}
210 err: vm.ErrFalseVMResult,
213 desc: "nonce exthash nonempty",
215 iss := txIssuance(t, tx, 0)
216 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
217 nonce.ExtHash = newHash(1)
219 err: errNonemptyExtHash,
222 desc: "nonce exthash nonempty, but that's OK",
225 iss := txIssuance(t, tx, 0)
226 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
227 nonce.ExtHash = newHash(1)
231 desc: "mismatched output source / mux dest position",
233 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
235 err: errMismatchedPosition,
238 desc: "mismatched output source and mux dest",
240 // For this test, it's necessary to construct a mostly
241 // identical second transaction in order to get a similar but
242 // not equal output entry for the mux to falsely point
243 // to. That entry must be added to the first tx's Entries map.
244 fixture.txOutputs[0].ReferenceData = []byte{1}
245 fixture2 := sample(t, fixture)
246 tx2 := legacy.NewTx(*fixture2.tx).Tx
247 out2ID := tx2.ResultIds[0]
248 out2 := tx2.Entries[*out2ID].(*bc.Output)
249 tx.Entries[*out2ID] = out2
250 mux.WitnessDestinations[0].Ref = out2ID
252 err: errMismatchedReference,
255 desc: "invalid mux destination position",
257 mux.WitnessDestinations[0].Position = 1
262 desc: "mismatched mux dest value / output source value",
264 outID := tx.ResultIds[0]
265 out := tx.Entries[*outID].(*bc.Output)
266 mux.WitnessDestinations[0].Value = &bc.AssetAmount{
267 AssetId: out.Source.Value.AssetId,
268 Amount: out.Source.Value.Amount + 1,
270 mux.Sources[0].Value.Amount++ // the mux must still balance
272 err: errMismatchedValue,
275 desc: "output exthash nonempty",
277 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
279 err: errNonemptyExtHash,
282 desc: "output exthash nonempty, but that's OK",
285 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
289 desc: "empty tx results",
293 err: errEmptyResults,
296 desc: "empty tx results, but that's OK",
303 desc: "tx header exthash nonempty",
305 tx.ExtHash = newHash(1)
307 err: errNonemptyExtHash,
310 desc: "tx header exthash nonempty, but that's OK",
313 tx.ExtHash = newHash(1)
317 desc: "issuance program failure",
319 iss := txIssuance(t, tx, 0)
320 iss.WitnessArguments[0] = []byte{}
322 err: vm.ErrFalseVMResult,
325 desc: "issuance exthash nonempty",
327 iss := txIssuance(t, tx, 0)
328 iss.ExtHash = newHash(1)
330 err: errNonemptyExtHash,
333 desc: "issuance exthash nonempty, but that's OK",
336 iss := txIssuance(t, tx, 0)
337 iss.ExtHash = newHash(1)
341 desc: "spend control program failure",
343 spend := txSpend(t, tx, 1)
344 spend.WitnessArguments[0] = []byte{}
346 err: vm.ErrFalseVMResult,
349 desc: "mismatched spent source/witness value",
351 spend := txSpend(t, tx, 1)
352 spentOutput := tx.Entries[*spend.SpentOutputId].(*bc.Output)
353 spentOutput.Source.Value = &bc.AssetAmount{
354 AssetId: spend.WitnessDestination.Value.AssetId,
355 Amount: spend.WitnessDestination.Value.Amount + 1,
358 err: errMismatchedValue,
361 desc: "spend exthash nonempty",
363 spend := txSpend(t, tx, 1)
364 spend.ExtHash = newHash(1)
366 err: errNonemptyExtHash,
369 desc: "spend exthash nonempty, but that's OK",
372 spend := txSpend(t, tx, 1)
373 spend.ExtHash = newHash(1)
378 for _, c := range cases {
379 t.Run(c.desc, func(t *testing.T) {
380 fixture = sample(t, nil)
381 tx = legacy.NewTx(*fixture.tx).Tx
382 vs = &validationState{
387 gasLeft: int64(80000),
390 cache: make(map[bc.Hash]error),
392 out := tx.Entries[*tx.ResultIds[0]].(*bc.Output)
393 muxID := out.Source.Ref
394 mux = tx.Entries[*muxID].(*bc.Mux)
399 err := checkValid(vs, tx.TxHeader)
401 if rootErr(err) != c.err {
402 t.Errorf("got error %s, want %s; validationState is:\n%s", err, c.err, spew.Sdump(vs))
408 func TestValidateBlock(t *testing.T) {
415 BlockHeader: &bc.BlockHeader{
418 Transactions: []*bc.Tx{mockCoinbaseTx(624000000000)},
424 BlockHeader: &bc.BlockHeader{
427 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
429 err: errWrongCoinbaseTransaction,
433 BlockHeader: &bc.BlockHeader{
435 SerializedSize: 88888888,
437 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
439 err: errWrongBlockSize,
443 for _, c := range cases {
444 txRoot, err := bc.MerkleRoot(c.block.Transactions)
446 t.Errorf("computing transaction merkle root", err)
449 c.block.TransactionsRoot = &txRoot
450 err = ValidateBlock(c.block, nil)
452 if rootErr(err) != c.err {
453 t.Errorf("got error %s, want %s", err, c.err)
458 func TestCoinbase(t *testing.T) {
459 CbTx := mockCoinbaseTx(5000000000)
460 errCbTx := legacy.MapTx(&legacy.TxData{
461 Outputs: []*legacy.TxOutput{
462 legacy.NewTxOutput(bc.AssetID{
463 V0: uint64(18446744073709551611),
464 V1: uint64(18446744073709551615),
465 V2: uint64(18446744073709551615),
466 V3: uint64(18446744073709551615),
467 }, 800000000000, []byte{1}, nil),
477 BlockHeader: &bc.BlockHeader{
480 Transactions: []*bc.Tx{errCbTx},
483 err: errWrongCoinbaseTransaction,
487 BlockHeader: &bc.BlockHeader{
490 Transactions: []*bc.Tx{CbTx},
497 BlockHeader: &bc.BlockHeader{
500 Transactions: []*bc.Tx{errCbTx},
503 err: errWrongCoinbaseAsset,
507 for _, c := range cases {
508 _, err := ValidateTx(c.tx, c.block)
510 if rootErr(err) != c.err {
511 t.Errorf("got error %s, want %s", err, c.err)
516 func TestBlockHeaderValid(t *testing.T) {
517 base := bc.NewBlockHeader(1, 1, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, 0, 0)
518 baseBytes, _ := proto.Marshal(base)
520 var bh bc.BlockHeader
534 for i, c := range cases {
535 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
536 proto.Unmarshal(baseBytes, &bh)
544 // A txFixture is returned by sample (below) to produce a sample
545 // transaction, which takes a separate, optional _input_ txFixture to
546 // affect the transaction that's built. The components of the
547 // transaction are the fields of txFixture.
548 type txFixture struct {
549 initialBlockID bc.Hash
550 issuanceProg bc.Program
551 issuanceArgs [][]byte
555 txInputs []*legacy.TxInput
556 txOutputs []*legacy.TxOutput
557 txMinTime, txMaxTime uint64
562 // Produces a sample transaction in a txFixture object (see above). A
563 // separate input txFixture can be used to alter the transaction
566 // The output of this function can be used as the input to a
567 // subsequent call to make iterative refinements to a test object.
569 // The default transaction produced is valid and has three inputs:
570 // - an issuance of 10 units
571 // - a spend of 20 units
572 // - a spend of 40 units
573 // and two outputs, one of 25 units and one of 45 units.
574 // All amounts are denominated in the same asset.
576 // The issuance program for the asset requires two numbers as
577 // arguments that add up to 5. The prevout control programs require
578 // two numbers each, adding to 9 and 13, respectively.
580 // The min and max times for the transaction are now +/- one minute.
581 func sample(tb testing.TB, in *txFixture) *txFixture {
587 if result.initialBlockID.IsZero() {
588 result.initialBlockID = *newHash(1)
590 if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
591 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
595 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
597 if len(result.issuanceArgs) == 0 {
598 result.issuanceArgs = [][]byte{[]byte{2}, []byte{3}}
600 if len(result.assetDef) == 0 {
601 result.assetDef = []byte{2}
603 if result.assetID.IsZero() {
604 refdatahash := hashData(result.assetDef)
605 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, &result.initialBlockID, result.issuanceProg.VmVersion, &refdatahash)
608 if result.txVersion == 0 {
611 if len(result.txInputs) == 0 {
612 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
616 args1 := [][]byte{[]byte{4}, []byte{5}}
618 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
622 args2 := [][]byte{[]byte{6}, []byte{7}}
624 result.txInputs = []*legacy.TxInput{
625 legacy.NewIssuanceInput([]byte{3}, 10, []byte{4}, result.initialBlockID, result.issuanceProg.Code, result.issuanceArgs, result.assetDef),
626 legacy.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1, *newHash(6), []byte{7}),
627 legacy.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2, *newHash(9), []byte{10}),
631 result.txInputs = append(result.txInputs, mockGasTxInput())
633 if len(result.txOutputs) == 0 {
634 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
638 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
643 result.txOutputs = []*legacy.TxOutput{
644 legacy.NewTxOutput(result.assetID, 25, cp1, []byte{11}),
645 legacy.NewTxOutput(result.assetID, 45, cp2, []byte{12}),
648 if result.txMinTime == 0 {
649 result.txMinTime = bc.Millis(time.Now().Add(-time.Minute))
651 if result.txMaxTime == 0 {
652 result.txMaxTime = bc.Millis(time.Now().Add(time.Minute))
654 if len(result.txRefData) == 0 {
655 result.txRefData = []byte{13}
658 result.tx = &legacy.TxData{
659 Version: result.txVersion,
660 Inputs: result.txInputs,
661 Outputs: result.txOutputs,
662 MinTime: result.txMinTime,
663 MaxTime: result.txMaxTime,
664 ReferenceData: result.txRefData,
670 func mockBlock() *bc.Block {
672 BlockHeader: &bc.BlockHeader{
678 func mockCoinbaseTx(amount uint64) *bc.Tx {
679 return legacy.MapTx(&legacy.TxData{
680 Outputs: []*legacy.TxOutput{
681 legacy.NewTxOutput(*consensus.BTMAssetID, amount, []byte{1}, nil),
686 func mockGasTxInput() *legacy.TxInput {
687 return legacy.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, []byte{byte(vm.OP_TRUE)}, *newHash(9), []byte{})
690 // Like errors.Root, but also unwraps vm.Error objects.
691 func rootErr(e error) error {
694 if e2, ok := e.(vm.Error); ok {
702 func hashData(data []byte) bc.Hash {
704 sha3pool.Sum256(b32[:], data)
705 return bc.NewHash(b32)
708 func newHash(n byte) *bc.Hash {
709 h := bc.NewHash([32]byte{n})
713 func newAssetID(n byte) *bc.AssetID {
714 a := bc.NewAssetID([32]byte{n})
718 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
719 id := tx.InputIDs[index]
720 res, err := tx.Issuance(id)
727 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
728 id := tx.InputIDs[index]
729 res, err := tx.Spend(id)