"fmt"
"math"
"testing"
- "time"
+ "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/bctest"
- "github.com/bytom/protocol/bc/legacy"
+ "github.com/bytom/protocol/bc/types"
"github.com/bytom/protocol/vm"
+ "github.com/bytom/protocol/vm/vmutil"
"github.com/bytom/testutil"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/golang/protobuf/proto"
)
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
// 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
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{
- block: nil,
+ block: mockBlock(),
tx: tx,
entryID: tx.ID,
- gas: &gasState{
- gasLeft: uint64(1000),
- gasUsed: 0,
+ gasStatus: &GasState{
+ GasLeft: int64(80000),
+ GasUsed: 0,
},
cache: make(map[bc.Hash]error),
}
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))
}
}
}
-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), nil)
- 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)
+ }
}
}
// 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
}
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 {
}
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 {
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 {