8 "github.com/davecgh/go-spew/spew"
9 "github.com/golang/protobuf/proto"
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"
21 const dirPath = "pseudohsm/testdata/pseudo"
24 spew.Config.DisableMethods = true
27 func TestGasStatus(t *testing.T) {
31 f func(*gasState) error
41 gasLeft: 10000 / GasRate,
45 f: func(input *gasState) error {
46 return input.setGas(10000)
61 f: func(input *gasState) error {
62 return input.setGas(-10000)
68 gasLeft: defaultGasLimit,
73 gasLeft: defaultGasLimit,
75 BTMValue: 80000000000,
77 f: func(input *gasState) error {
78 return input.setGas(80000000000)
93 f: func(input *gasState) error {
94 return input.updateUsage(-1)
109 f: func(input *gasState) error {
110 return input.updateUsage(9999)
116 for _, c := range cases {
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)
127 func TestTxValidation(t *testing.T) {
133 // the mux from tx, pulled out for convenience
138 desc string // description of the test case
139 f func() // function to adjust tx, vs, and/or mux
140 err error // expected error
146 desc: "failing mux program",
148 mux.Program.Code = []byte{byte(vm.OP_FALSE)}
150 err: vm.ErrFalseVMResult,
153 desc: "unbalanced mux amounts",
155 mux.Sources[0].Value.Amount++
156 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
157 iss.WitnessDestination.Value.Amount++
162 desc: "overflowing mux source amounts",
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
171 desc: "underflowing mux destination amounts",
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
183 desc: "unbalanced mux assets",
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)
192 desc: "nonempty mux exthash",
194 mux.ExtHash = newHash(1)
196 err: errNonemptyExtHash,
199 desc: "nonempty mux exthash, but that's OK",
202 mux.ExtHash = newHash(1)
206 desc: "failing nonce program",
208 iss := txIssuance(t, tx, 0)
209 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
210 nonce.Program.Code = []byte{byte(vm.OP_FALSE)}
212 err: vm.ErrFalseVMResult,
215 desc: "nonce exthash nonempty",
217 iss := txIssuance(t, tx, 0)
218 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
219 nonce.ExtHash = newHash(1)
221 err: errNonemptyExtHash,
224 desc: "nonce exthash nonempty, but that's OK",
227 iss := txIssuance(t, tx, 0)
228 nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
229 nonce.ExtHash = newHash(1)
233 desc: "mismatched output source / mux dest position",
235 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
237 err: errMismatchedPosition,
240 desc: "mismatched output source and mux dest",
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
254 err: errMismatchedReference,
257 desc: "invalid mux destination position",
259 mux.WitnessDestinations[0].Position = 1
264 desc: "mismatched mux dest value / output source value",
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,
272 mux.Sources[0].Value.Amount++ // the mux must still balance
274 err: errMismatchedValue,
277 desc: "output exthash nonempty",
279 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
281 err: errNonemptyExtHash,
284 desc: "output exthash nonempty, but that's OK",
287 tx.Entries[*tx.ResultIds[0]].(*bc.Output).ExtHash = newHash(1)
291 desc: "empty tx results",
295 err: errEmptyResults,
298 desc: "empty tx results, but that's OK",
305 desc: "tx header exthash nonempty",
307 tx.ExtHash = newHash(1)
309 err: errNonemptyExtHash,
312 desc: "tx header exthash nonempty, but that's OK",
315 tx.ExtHash = newHash(1)
319 desc: "issuance program failure",
321 iss := txIssuance(t, tx, 0)
322 iss.WitnessArguments[0] = []byte{}
324 err: vm.ErrFalseVMResult,
327 desc: "issuance exthash nonempty",
329 iss := txIssuance(t, tx, 0)
330 iss.ExtHash = newHash(1)
332 err: errNonemptyExtHash,
335 desc: "issuance exthash nonempty, but that's OK",
338 iss := txIssuance(t, tx, 0)
339 iss.ExtHash = newHash(1)
343 desc: "spend control program failure",
345 spend := txSpend(t, tx, 1)
346 spend.WitnessArguments[0] = []byte{}
348 err: vm.ErrFalseVMResult,
351 desc: "mismatched spent source/witness value",
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,
360 err: errMismatchedValue,
363 desc: "spend exthash nonempty",
365 spend := txSpend(t, tx, 1)
366 spend.ExtHash = newHash(1)
368 err: errNonemptyExtHash,
371 desc: "spend exthash nonempty, but that's OK",
374 spend := txSpend(t, tx, 1)
375 spend.ExtHash = newHash(1)
380 for _, c := range cases {
382 t.Run(c.desc, func(t *testing.T) {
383 fixture = sample(t, nil)
384 tx = legacy.NewTx(*fixture.tx).Tx
385 vs = &validationState{
390 gasLeft: int64(80000),
393 cache: make(map[bc.Hash]error),
396 out := tx.Entries[*tx.ResultIds[0]].(*bc.Output)
397 muxID := out.Source.Ref
398 mux = tx.Entries[*muxID].(*bc.Mux)
403 err := checkValid(vs, tx.TxHeader)
405 if rootErr(err) != c.err {
406 t.Errorf("got error %s, want %s; validationState is:\n%s", err, c.err, spew.Sdump(vs))
412 func TestValidateBlock(t *testing.T) {
419 BlockHeader: &bc.BlockHeader{
421 Bits: 2305843009230471167,
423 Transactions: []*bc.Tx{mockCoinbaseTx(1470000000000000000)},
429 BlockHeader: &bc.BlockHeader{
431 Bits: 2305843009230471167,
433 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
435 err: errWrongCoinbaseTransaction,
439 BlockHeader: &bc.BlockHeader{
441 Bits: 2305843009230471167,
442 SerializedSize: 88888888,
444 Transactions: []*bc.Tx{mockCoinbaseTx(1)},
446 err: errWrongBlockSize,
450 for _, c := range cases {
451 txRoot, err := bc.MerkleRoot(c.block.Transactions)
453 t.Errorf("computing transaction merkle root", err)
456 c.block.BlockHeader.TransactionStatus = bc.NewTransactionStatus()
457 c.block.TransactionsRoot = &txRoot
459 if err = ValidateBlock(c.block, nil); rootErr(err) != c.err {
460 t.Errorf("got error %s, want %s", err, c.err)
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),
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),
488 BlockHeader: &bc.BlockHeader{
491 Transactions: []*bc.Tx{errCbTx},
495 err: errWrongCoinbaseTransaction,
499 BlockHeader: &bc.BlockHeader{
502 Transactions: []*bc.Tx{CbTx},
510 BlockHeader: &bc.BlockHeader{
513 Transactions: []*bc.Tx{errCbTx},
517 err: errWrongCoinbaseAsset,
521 for _, c := range cases {
522 _, gasVaild, err := ValidateTx(c.tx, c.block)
524 if rootErr(err) != c.err {
525 t.Errorf("got error %s, want %s", err, c.err)
527 if c.gasVaild != gasVaild {
528 t.Errorf("got gasVaild %s, want %s", gasVaild, c.gasVaild)
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)
537 var bh bc.BlockHeader
551 for i, c := range cases {
552 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
553 proto.Unmarshal(baseBytes, &bh)
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
572 txInputs []*legacy.TxInput
573 txOutputs []*legacy.TxOutput
578 // Produces a sample transaction in a txFixture object (see above). A
579 // separate input txFixture can be used to alter the transaction
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.
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.
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.
596 // The min and max times for the transaction are now +/- one minute.
597 func sample(tb testing.TB, in *txFixture) *txFixture {
603 if result.initialBlockID.IsZero() {
604 result.initialBlockID = *newHash(1)
606 if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
607 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
611 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
613 if len(result.issuanceArgs) == 0 {
614 result.issuanceArgs = [][]byte{[]byte{2}, []byte{3}}
616 if len(result.assetDef) == 0 {
617 result.assetDef = []byte{2}
619 if result.assetID.IsZero() {
620 refdatahash := hashData(result.assetDef)
621 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, &result.initialBlockID, result.issuanceProg.VmVersion, &refdatahash)
624 if result.txVersion == 0 {
627 if len(result.txInputs) == 0 {
628 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
632 args1 := [][]byte{[]byte{4}, []byte{5}}
634 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
638 args2 := [][]byte{[]byte{6}, []byte{7}}
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}),
647 result.txInputs = append(result.txInputs, mockGasTxInput())
649 if len(result.txOutputs) == 0 {
650 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
654 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
659 result.txOutputs = []*legacy.TxOutput{
660 legacy.NewTxOutput(result.assetID, 25, cp1, []byte{11}),
661 legacy.NewTxOutput(result.assetID, 45, cp2, []byte{12}),
664 if len(result.txRefData) == 0 {
665 result.txRefData = []byte{13}
668 result.tx = &legacy.TxData{
669 Version: result.txVersion,
670 Inputs: result.txInputs,
671 Outputs: result.txOutputs,
672 ReferenceData: result.txRefData,
678 func mockBlock() *bc.Block {
680 BlockHeader: &bc.BlockHeader{
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),
692 Outputs: []*legacy.TxOutput{
693 legacy.NewTxOutput(*consensus.BTMAssetID, amount, cp, nil),
698 func mockGasTxInput() *legacy.TxInput {
699 cp, _ := vmutil.DefaultCoinbaseProgram()
700 return legacy.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp, *newHash(9), []byte{})
703 // Like errors.Root, but also unwraps vm.Error objects.
704 func rootErr(e error) error {
707 if e2, ok := e.(vm.Error); ok {
715 func hashData(data []byte) bc.Hash {
717 sha3pool.Sum256(b32[:], data)
718 return bc.NewHash(b32)
721 func newHash(n byte) *bc.Hash {
722 h := bc.NewHash([32]byte{n})
726 func newAssetID(n byte) *bc.AssetID {
727 a := bc.NewAssetID([32]byte{n})
731 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
732 id := tx.InputIDs[index]
733 res, err := tx.Issuance(id)
740 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
741 id := tx.InputIDs[index]
742 res, err := tx.Spend(id)