OSDN Git Service

update master (#487)
[bytom/bytom.git] / protocol / validation / validation_test.go
index a9d2c94..182db3a 100644 (file)
@@ -4,24 +4,124 @@ import (
        "fmt"
        "math"
        "testing"
-       "time"
-
-       "github.com/blockchain/crypto/sha3pool"
-       "github.com/blockchain/errors"
-       "github.com/blockchain/protocol/bc"
-       "github.com/blockchain/protocol/bc/bctest"
-       "github.com/blockchain/protocol/bc/legacy"
-       "github.com/blockchain/protocol/vm"
-       "github.com/blockchain/testutil"
 
        "github.com/davecgh/go-spew/spew"
        "github.com/golang/protobuf/proto"
+
+       "github.com/bytom/consensus"
+       "github.com/bytom/crypto/sha3pool"
+       "github.com/bytom/errors"
+       "github.com/bytom/protocol/bc"
+       "github.com/bytom/protocol/bc/types"
+       "github.com/bytom/protocol/vm"
+       "github.com/bytom/protocol/vm/vmutil"
+       "github.com/bytom/testutil"
 )
 
 func init() {
        spew.Config.DisableMethods = true
 }
 
+func TestGasStatus(t *testing.T) {
+       cases := []struct {
+               input  *GasState
+               output *GasState
+               f      func(*GasState) error
+               err    error
+       }{
+               {
+                       input: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       output: &GasState{
+                               GasLeft:  10000 / consensus.VMGasRate,
+                               GasUsed:  0,
+                               BTMValue: 10000,
+                       },
+                       f: func(input *GasState) error {
+                               return input.setGas(10000, 0)
+                       },
+                       err: nil,
+               },
+               {
+                       input: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       output: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       f: func(input *GasState) error {
+                               return input.setGas(-10000, 0)
+                       },
+                       err: errGasCalculate,
+               },
+               {
+                       input: &GasState{
+                               GasLeft:  consensus.DefaultGasCredit,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       output: &GasState{
+                               GasLeft:  100000,
+                               GasUsed:  0,
+                               BTMValue: 80000000000,
+                       },
+                       f: func(input *GasState) error {
+                               return input.setGas(80000000000, 0)
+                       },
+                       err: nil,
+               },
+               {
+                       input: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       output: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       f: func(input *GasState) error {
+                               return input.updateUsage(-1)
+                       },
+                       err: errGasCalculate,
+               },
+               {
+                       input: &GasState{
+                               GasLeft:  10000,
+                               GasUsed:  0,
+                               BTMValue: 0,
+                       },
+                       output: &GasState{
+                               GasLeft:  9999,
+                               GasUsed:  1,
+                               BTMValue: 0,
+                       },
+                       f: func(input *GasState) error {
+                               return input.updateUsage(9999)
+                       },
+                       err: nil,
+               },
+       }
+
+       for i, c := range cases {
+               err := c.f(c.input)
+
+               if rootErr(err) != c.err {
+                       t.Errorf("case %d: got error %s, want %s", i, err, c.err)
+               } else if *c.input != *c.output {
+                       t.Errorf("case %d: gasStatus %v, want %v;", i, c.input, c.output)
+               }
+       }
+}
+
 func TestTxValidation(t *testing.T) {
        var (
                tx      *bc.Tx
@@ -128,46 +228,6 @@ func TestTxValidation(t *testing.T) {
                        },
                },
                {
-                       desc: "nonce timerange misordered",
-                       f: func() {
-                               iss := txIssuance(t, tx, 0)
-                               nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
-                               tr := tx.Entries[*nonce.TimeRangeId].(*bc.TimeRange)
-                               tr.MinTimeMs = tr.MaxTimeMs + 1
-                       },
-                       err: errBadTimeRange,
-               },
-               {
-                       desc: "nonce timerange disagrees with tx timerange",
-                       f: func() {
-                               iss := txIssuance(t, tx, 0)
-                               nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
-                               tr := tx.Entries[*nonce.TimeRangeId].(*bc.TimeRange)
-                               tr.MaxTimeMs = tx.MaxTimeMs - 1
-                       },
-                       err: errBadTimeRange,
-               },
-               {
-                       desc: "nonce timerange exthash nonempty",
-                       f: func() {
-                               iss := txIssuance(t, tx, 0)
-                               nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
-                               tr := tx.Entries[*nonce.TimeRangeId].(*bc.TimeRange)
-                               tr.ExtHash = newHash(1)
-                       },
-                       err: errNonemptyExtHash,
-               },
-               {
-                       desc: "nonce timerange exthash nonempty, but that's OK",
-                       f: func() {
-                               tx.Version = 2
-                               iss := txIssuance(t, tx, 0)
-                               nonce := tx.Entries[*iss.AnchorId].(*bc.Nonce)
-                               tr := tx.Entries[*nonce.TimeRangeId].(*bc.TimeRange)
-                               tr.ExtHash = newHash(1)
-                       },
-               },
-               {
                        desc: "mismatched output source / mux dest position",
                        f: func() {
                                tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
@@ -181,9 +241,8 @@ func TestTxValidation(t *testing.T) {
                                // identical second transaction in order to get a similar but
                                // not equal output entry for the mux to falsely point
                                // to. That entry must be added to the first tx's Entries map.
-                               fixture.txOutputs[0].ReferenceData = []byte{1}
                                fixture2 := sample(t, fixture)
-                               tx2 := legacy.NewTx(*fixture2.tx).Tx
+                               tx2 := types.NewTx(*fixture2.tx).Tx
                                out2ID := tx2.ResultIds[0]
                                out2 := tx2.Entries[*out2ID].(*bc.Output)
                                tx.Entries[*out2ID] = out2
@@ -226,13 +285,6 @@ func TestTxValidation(t *testing.T) {
                        },
                },
                {
-                       desc: "misordered tx time range",
-                       f: func() {
-                               tx.MinTimeMs = tx.MaxTimeMs + 1
-                       },
-                       err: errBadTimeRange,
-               },
-               {
                        desc: "empty tx results",
                        f: func() {
                                tx.ResultIds = nil
@@ -261,21 +313,6 @@ func TestTxValidation(t *testing.T) {
                        },
                },
                {
-                       desc: "wrong blockchain",
-                       f: func() {
-                               vs.blockchainID = *newHash(2)
-                       },
-                       err: errWrongBlockchain,
-               },
-               {
-                       desc: "issuance asset ID mismatch",
-                       f: func() {
-                               iss := txIssuance(t, tx, 0)
-                               iss.Value.AssetId = newAssetID(1)
-                       },
-                       err: errMismatchedAssetID,
-               },
-               {
                        desc: "issuance program failure",
                        f: func() {
                                iss := txIssuance(t, tx, 0)
@@ -340,12 +377,16 @@ func TestTxValidation(t *testing.T) {
        for _, c := range cases {
                t.Run(c.desc, func(t *testing.T) {
                        fixture = sample(t, nil)
-                       tx = legacy.NewTx(*fixture.tx).Tx
+                       tx = types.NewTx(*fixture.tx).Tx
                        vs = &validationState{
-                               blockchainID: fixture.initialBlockID,
-                               tx:           tx,
-                               entryID:      tx.ID,
-                               cache:        make(map[bc.Hash]error),
+                               block:   mockBlock(),
+                               tx:      tx,
+                               entryID: tx.ID,
+                               gasStatus: &GasState{
+                                       GasLeft: int64(80000),
+                                       GasUsed: 0,
+                               },
+                               cache: make(map[bc.Hash]error),
                        }
                        out := tx.Entries[*tx.ResultIds[0]].(*bc.Output)
                        muxID := out.Source.Ref
@@ -355,6 +396,7 @@ func TestTxValidation(t *testing.T) {
                                c.f()
                        }
                        err := checkValid(vs, tx.TxHeader)
+
                        if rootErr(err) != c.err {
                                t.Errorf("got error %s, want %s; validationState is:\n%s", err, c.err, spew.Sdump(vs))
                        }
@@ -362,20 +404,142 @@ func TestTxValidation(t *testing.T) {
        }
 }
 
-func TestNoncelessIssuance(t *testing.T) {
-       tx := bctest.NewIssuanceTx(t, bc.EmptyStringHash, func(tx *legacy.Tx) {
-               // Remove the issuance nonce.
-               tx.Inputs[0].TypedInput.(*legacy.IssuanceInput).Nonce = nil
+func TestValidateBlock(t *testing.T) {
+       cases := []struct {
+               block *bc.Block
+               err   error
+       }{
+               {
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{
+                                       Height:          0,
+                                       Bits:            2305843009230471167,
+                                       PreviousBlockId: &bc.Hash{},
+                               },
+                               Transactions: []*bc.Tx{mockCoinbaseTx(1470000000000000000)},
+                       },
+                       err: nil,
+               },
+               {
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{
+                                       Height:          0,
+                                       Bits:            2305843009230471167,
+                                       PreviousBlockId: &bc.Hash{},
+                               },
+                               Transactions: []*bc.Tx{mockCoinbaseTx(1)},
+                       },
+                       err: errWrongCoinbaseTransaction,
+               },
+       }
+
+       txStatus := bc.NewTransactionStatus()
+       txStatusHash := bc.EntryID(txStatus)
+
+       for _, c := range cases {
+               txRoot, err := bc.MerkleRoot(c.block.Transactions)
+               if err != nil {
+                       t.Errorf("computing transaction merkle root error: %v", err)
+                       continue
+               }
+               c.block.BlockHeader.TransactionStatus = bc.NewTransactionStatus()
+               c.block.TransactionsRoot = &txRoot
+               c.block.TransactionStatusHash = &txStatusHash
+
+               if err = ValidateBlock(c.block, nil, &bc.Hash{}, nil); rootErr(err) != c.err {
+                       t.Errorf("got error %s, want %s", err, c.err)
+               }
+       }
+}
+
+func TestCoinbase(t *testing.T) {
+       CbTx := mockCoinbaseTx(5000000000)
+       cases := []struct {
+               block    *bc.Block
+               tx       *bc.Tx
+               GasVaild bool
+               err      error
+       }{
+               {
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{
+                                       Height: 666,
+                               },
+                               Transactions: []*bc.Tx{CbTx},
+                       },
+                       tx:       CbTx,
+                       GasVaild: true,
+                       err:      nil,
+               },
+       }
+
+       for i, c := range cases {
+               gasStatus, err := ValidateTx(c.tx, c.block)
+
+               if rootErr(err) != c.err {
+                       t.Errorf("#%d got error %s, want %s", i, err, c.err)
+               }
+               if c.GasVaild != gasStatus.GasVaild {
+                       t.Errorf("#%d got GasVaild %t, want %t", i, gasStatus.GasVaild, c.GasVaild)
+               }
+       }
+}
+
+func TestTimeRange(t *testing.T) {
+       cases := []struct {
+               timeRange uint64
+               err       bool
+       }{
+               {
+                       timeRange: 0,
+                       err:       false,
+               },
+               {
+                       timeRange: 334,
+                       err:       false,
+               },
+               {
+                       timeRange: 332,
+                       err:       true,
+               },
+               {
+                       timeRange: 1521625824,
+                       err:       false,
+               },
+               {
+                       timeRange: 1421625824,
+                       err:       true,
+               },
+       }
+
+       block := &bc.Block{
+               BlockHeader: &bc.BlockHeader{
+                       Height:    333,
+                       Timestamp: 1521625823,
+               },
+       }
+
+       tx := types.MapTx(&types.TxData{
+               SerializedSize: 1,
+               TimeRange:      0,
+               Inputs: []*types.TxInput{
+                       mockGasTxInput(),
+               },
+               Outputs: []*types.TxOutput{
+                       types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
+               },
        })
 
-       err := ValidateTx(legacy.MapTx(&tx.TxData), bc.EmptyStringHash)
-       if errors.Root(err) != bc.ErrMissingEntry {
-               t.Fatalf("got %s, want %s", err, bc.ErrMissingEntry)
+       for i, c := range cases {
+               tx.TimeRange = c.timeRange
+               if _, err := ValidateTx(tx, block); (err != nil) != c.err {
+                       t.Errorf("#%d got error %s, want %s", i, !c.err, c.err)
+               }
        }
 }
 
 func TestBlockHeaderValid(t *testing.T) {
-       base := bc.NewBlockHeader(1, 1, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, nil)
+       base := bc.NewBlockHeader(1, 1, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, 0, 0)
        baseBytes, _ := proto.Marshal(base)
 
        var bh bc.BlockHeader
@@ -390,12 +554,6 @@ func TestBlockHeaderValid(t *testing.T) {
                                bh.Version = 2
                        },
                },
-               {
-                       f: func() {
-                               bh.ExtHash = newHash(1)
-                       },
-                       err: errNonemptyExtHash,
-               },
        }
 
        for i, c := range cases {
@@ -404,10 +562,6 @@ func TestBlockHeaderValid(t *testing.T) {
                        if c.f != nil {
                                c.f()
                        }
-                       err := checkValidBlockHeader(&bh)
-                       if err != c.err {
-                               t.Errorf("got error %s, want %s; bh is:\n%s", err, c.err, spew.Sdump(bh))
-                       }
                })
        }
 }
@@ -417,17 +571,16 @@ func TestBlockHeaderValid(t *testing.T) {
 // affect the transaction that's built. The components of the
 // transaction are the fields of txFixture.
 type txFixture struct {
-       initialBlockID       bc.Hash
-       issuanceProg         bc.Program
-       issuanceArgs         [][]byte
-       assetDef             []byte
-       assetID              bc.AssetID
-       txVersion            uint64
-       txInputs             []*legacy.TxInput
-       txOutputs            []*legacy.TxOutput
-       txMinTime, txMaxTime uint64
-       txRefData            []byte
-       tx                   *legacy.TxData
+       initialBlockID bc.Hash
+       issuanceProg   bc.Program
+       issuanceArgs   [][]byte
+       assetDef       []byte
+       assetID        bc.AssetID
+       txVersion      uint64
+       txInputs       []*types.TxInput
+       txOutputs      []*types.TxOutput
+       txRefData      []byte
+       tx             *types.TxData
 }
 
 // Produces a sample transaction in a txFixture object (see above). A
@@ -473,7 +626,7 @@ func sample(tb testing.TB, in *txFixture) *txFixture {
        }
        if result.assetID.IsZero() {
                refdatahash := hashData(result.assetDef)
-               result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, &result.initialBlockID, result.issuanceProg.VmVersion, &refdatahash)
+               result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, result.issuanceProg.VmVersion, &refdatahash)
        }
 
        if result.txVersion == 0 {
@@ -492,12 +645,15 @@ func sample(tb testing.TB, in *txFixture) *txFixture {
                }
                args2 := [][]byte{[]byte{6}, []byte{7}}
 
-               result.txInputs = []*legacy.TxInput{
-                       legacy.NewIssuanceInput([]byte{3}, 10, []byte{4}, result.initialBlockID, result.issuanceProg.Code, result.issuanceArgs, result.assetDef),
-                       legacy.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1, *newHash(6), []byte{7}),
-                       legacy.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2, *newHash(9), []byte{10}),
+               result.txInputs = []*types.TxInput{
+                       types.NewIssuanceInput([]byte{3}, 10, result.issuanceProg.Code, result.issuanceArgs, result.assetDef),
+                       types.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1),
+                       types.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2),
                }
        }
+
+       result.txInputs = append(result.txInputs, mockGasTxInput())
+
        if len(result.txOutputs) == 0 {
                cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
                if err != nil {
@@ -508,33 +664,50 @@ func sample(tb testing.TB, in *txFixture) *txFixture {
                        tb.Fatal(err)
                }
 
-               result.txOutputs = []*legacy.TxOutput{
-                       legacy.NewTxOutput(result.assetID, 25, cp1, []byte{11}),
-                       legacy.NewTxOutput(result.assetID, 45, cp2, []byte{12}),
+               result.txOutputs = []*types.TxOutput{
+                       types.NewTxOutput(result.assetID, 25, cp1),
+                       types.NewTxOutput(result.assetID, 45, cp2),
                }
        }
-       if result.txMinTime == 0 {
-               result.txMinTime = bc.Millis(time.Now().Add(-time.Minute))
-       }
-       if result.txMaxTime == 0 {
-               result.txMaxTime = bc.Millis(time.Now().Add(time.Minute))
-       }
        if len(result.txRefData) == 0 {
                result.txRefData = []byte{13}
        }
 
-       result.tx = &legacy.TxData{
-               Version:       result.txVersion,
-               Inputs:        result.txInputs,
-               Outputs:       result.txOutputs,
-               MinTime:       result.txMinTime,
-               MaxTime:       result.txMaxTime,
-               ReferenceData: result.txRefData,
+       result.tx = &types.TxData{
+               Version: result.txVersion,
+               Inputs:  result.txInputs,
+               Outputs: result.txOutputs,
        }
 
        return &result
 }
 
+func mockBlock() *bc.Block {
+       return &bc.Block{
+               BlockHeader: &bc.BlockHeader{
+                       Height: 666,
+               },
+       }
+}
+
+func mockCoinbaseTx(amount uint64) *bc.Tx {
+       cp, _ := vmutil.DefaultCoinbaseProgram()
+       return types.MapTx(&types.TxData{
+               SerializedSize: 1,
+               Inputs: []*types.TxInput{
+                       types.NewCoinbaseInput(nil),
+               },
+               Outputs: []*types.TxOutput{
+                       types.NewTxOutput(*consensus.BTMAssetID, amount, cp),
+               },
+       })
+}
+
+func mockGasTxInput() *types.TxInput {
+       cp, _ := vmutil.DefaultCoinbaseProgram()
+       return types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)
+}
+
 // Like errors.Root, but also unwraps vm.Error objects.
 func rootErr(e error) error {
        for {