X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fvalidation%2Fvalidation_test.go;h=182db3aff12ab306081e83ff1b8a0ca7f71aa7ed;hb=01180396e6e36d20c8a666286c985637e6186bfd;hp=e369d6c374b21a146522afea7ab0971018bf3d51;hpb=d7457a5e2a432a11809e113b7b1d31a32fec2f61;p=bytom%2Fbytom.git diff --git a/protocol/validation/validation_test.go b/protocol/validation/validation_test.go index e369d6c3..182db3af 100644 --- a/protocol/validation/validation_test.go +++ b/protocol/validation/validation_test.go @@ -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,13 +313,6 @@ func TestTxValidation(t *testing.T) { }, }, { - desc: "wrong blockchain", - f: func() { - vs.blockchainID = *newHash(2) - }, - err: errWrongBlockchain, - }, - { desc: "issuance program failure", f: func() { iss := txIssuance(t, tx, 0) @@ -332,15 +377,14 @@ 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, - gas: &gasState{ - gasLeft: uint64(1000), - gasUsed: 0, - maxGas: uint64(1000), + block: mockBlock(), + tx: tx, + entryID: tx.ID, + gasStatus: &GasState{ + GasLeft: int64(80000), + GasUsed: 0, }, cache: make(map[bc.Hash]error), } @@ -352,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)) } @@ -359,15 +404,137 @@ 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) + } } } @@ -404,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 @@ -460,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 { @@ -479,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 { @@ -495,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 {