OSDN Git Service

update for the validation level test
authorColt <colt@ColtdeMBP.lan>
Fri, 25 Aug 2017 12:09:53 +0000 (20:09 +0800)
committerColt <colt@ColtdeMBP.lan>
Fri, 25 Aug 2017 12:09:53 +0000 (20:09 +0800)
protocol/bc/blockheader.go
protocol/validation/validation.go
protocol/validation/validation_test.go
protocol/validation/vmcontext.go

index 0eb22b2..1b32130 100644 (file)
@@ -15,6 +15,10 @@ func (bh *BlockHeader) writeForHash(w io.Writer) {
        mustWriteForHash(w, bh.AssetsRoot)
 }
 
+func (bh *BlockHeader) BlockReward() uint64 {
+       return uint64(5000000000)
+}
+
 // NewBlockHeader creates a new BlockHeader and populates
 // its body.
 func NewBlockHeader(version, height uint64, previousBlockID *Hash, timestampMS uint64, transactionsRoot, assetsRoot *Hash, nonce, bits uint64) *BlockHeader {
index 41724d8..e52233e 100644 (file)
@@ -10,8 +10,8 @@ import (
 )
 
 const (
-       defaultGasLimit = uint64(80000)
-       gasRate         = uint64(1000)
+       defaultGasLimit = int64(80000)
+       gasRate         = int64(1000)
 )
 
 var BTMAssetID = &bc.AssetID{
@@ -22,22 +22,38 @@ var BTMAssetID = &bc.AssetID{
 }
 
 type gasState struct {
-       gasLeft  uint64
-       gasUsed  uint64
-       BTMValue uint64
+       gasLeft  int64
+       gasUsed  int64
+       BTMValue int64
 }
 
-func (g *gasState) setGas(BTMValue uint64) {
+func (g *gasState) setGas(BTMValue int64) error {
+       if BTMValue < 0 {
+               return errGasCalculate
+       }
        g.BTMValue = BTMValue
-       gasAmount := BTMValue / gasRate
-       if gasAmount < defaultGasLimit {
-               g.gasLeft = gasAmount
+
+       if gasAmount, ok := checked.DivInt64(BTMValue, gasRate); ok {
+               if gasAmount < defaultGasLimit {
+                       g.gasLeft = gasAmount
+               }
+       } else {
+               return errGasCalculate
        }
+       return nil
 }
 
-func (g *gasState) updateUsage(gasLeft int64) {
-       g.gasUsed += g.gasLeft - uint64(gasLeft)
-       g.gasLeft = uint64(gasLeft)
+func (g *gasState) updateUsage(gasLeft int64) error {
+       if gasLeft < 0 {
+               return errGasCalculate
+       }
+       if gasUsed, ok := checked.SubInt64(g.gasLeft, gasLeft); ok {
+               g.gasUsed += gasUsed
+               g.gasLeft = gasLeft
+       } else {
+               return errGasCalculate
+       }
+       return nil
 }
 
 // validationState contains the context that must propagate through
@@ -65,6 +81,7 @@ type validationState struct {
 }
 
 var (
+       errGasCalculate             = errors.New("gas usage calculate got a math error")
        errEmptyResults             = errors.New("transaction has no results")
        errMismatchedAssetID        = errors.New("mismatched asset id")
        errMismatchedBlock          = errors.New("mismatched block")
@@ -163,10 +180,11 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                }
 
                if amount, ok := parity[*BTMAssetID]; ok {
-                       vs.gas.setGas(uint64(amount))
+                       if err = vs.gas.setGas(amount); err != nil {
+                               return err
+                       }
                } else {
-                       //TODO: uncommon this code after fix the unit test
-                       //return errors.WithDetailf(errNoGas, "transaction should have BTM asset as gas input")
+                       vs.gas.setGas(0)
                }
 
                for assetID, amount := range parity {
@@ -175,11 +193,13 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                        }
                }
 
-               gasLeft, err := vm.Verify(NewTxVMContext(vs.tx, e, e.Program, e.WitnessArguments), int64(vs.gas.gasLeft))
-               vs.gas.updateUsage(gasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.Program, e.WitnessArguments), vs.gas.gasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking mux program")
                }
+               if err = vs.gas.updateUsage(gasLeft); err != nil {
+                       return err
+               }
 
                for i, src := range e.Sources {
                        vs2 := *vs
@@ -204,11 +224,13 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
 
        case *bc.Nonce:
                //TODO: add block heigh range check on the control program
-               gasLeft, err := vm.Verify(NewTxVMContext(vs.tx, e, e.Program, e.WitnessArguments), int64(vs.gas.gasLeft))
-               vs.gas.updateUsage(gasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.Program, e.WitnessArguments), vs.gas.gasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking nonce program")
                }
+               if err = vs.gas.updateUsage(gasLeft); err != nil {
+                       return err
+               }
 
                if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
                        return errNonemptyExtHash
@@ -249,11 +271,13 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                        return errors.Wrapf(bc.ErrMissingEntry, "entry for issuance anchor %x not found", e.AnchorId.Bytes())
                }
 
-               gasLeft, err := vm.Verify(NewTxVMContext(vs.tx, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), int64(vs.gas.gasLeft))
-               vs.gas.updateUsage(gasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), vs.gas.gasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking issuance program")
                }
+               if err = vs.gas.updateUsage(gasLeft); err != nil {
+                       return err
+               }
 
                var anchored *bc.Hash
                switch a := anchor.(type) {
@@ -300,11 +324,13 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                if err != nil {
                        return errors.Wrap(err, "getting spend prevout")
                }
-               gasLeft, err := vm.Verify(NewTxVMContext(vs.tx, e, spentOutput.ControlProgram, e.WitnessArguments), int64(vs.gas.gasLeft))
-               vs.gas.updateUsage(gasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gas.gasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking control program")
                }
+               if err = vs.gas.updateUsage(gasLeft); err != nil {
+                       return err
+               }
 
                eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value)
                if err != nil {
@@ -480,7 +506,7 @@ func ValidateBlock(b, prev *bc.Block) error {
                }
        }
 
-       coinbaseValue := uint64(0)
+       coinbaseValue := b.BlockHeader.BlockReward()
        for i, tx := range b.Transactions {
                if b.Version == 1 && tx.Version != 1 {
                        return errors.WithDetailf(errTxVersion, "block version %d, transaction version %d", b.Version, tx.Version)
@@ -496,17 +522,17 @@ func ValidateBlock(b, prev *bc.Block) error {
                if err != nil {
                        return errors.Wrapf(err, "validity of transaction %d of %d", i, len(b.Transactions))
                }
-               coinbaseValue += *txBTMValue
+               coinbaseValue += uint64(txBTMValue)
        }
 
        // check the coinbase output entry value
-       coinbaseOutput := b.Transactions[0].Entries[b.Transactions[0].SpentOutputIDs[0]]
-       switch coinbaseOutput := coinbaseOutput.(type) {
-       case *bc.Output:
-               if coinbaseOutput.Source.Value.Amount != coinbaseValue {
+       cbTx := b.Transactions[0]
+       cbOutput := cbTx.Entries[cbTx.SpentOutputIDs[0]]
+       if cbOutput, ok := cbOutput.(*bc.Output); ok {
+               if cbOutput.Source.Value.Amount != coinbaseValue {
                        return errWrongCoinbaseTransaction
                }
-       default:
+       } else {
                return errWrongCoinbaseTransaction
        }
 
@@ -540,7 +566,7 @@ func validateBlockAgainstPrev(b, prev *bc.Block) error {
 }
 
 // ValidateTx validates a transaction.
-func ValidateTx(tx *bc.Tx, block *bc.Block) (*uint64, error) {
+func ValidateTx(tx *bc.Tx, block *bc.Block) (int64, error) {
        //TODO: handle the gas limit
        vs := &validationState{
                block:   block,
@@ -552,5 +578,6 @@ func ValidateTx(tx *bc.Tx, block *bc.Block) (*uint64, error) {
                cache: make(map[bc.Hash]error),
        }
 
-       return &vs.gas.BTMValue, checkValid(vs, tx.TxHeader)
+       err := checkValid(vs, tx.TxHeader)
+       return vs.gas.BTMValue, err
 }
index 0e85cf0..2bf14e4 100644 (file)
@@ -9,8 +9,8 @@ import (
        "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/validation"
        "github.com/bytom/protocol/vm"
        "github.com/bytom/testutil"
 
@@ -280,11 +280,11 @@ func TestTxValidation(t *testing.T) {
                        fixture = sample(t, nil)
                        tx = legacy.NewTx(*fixture.tx).Tx
                        vs = &validationState{
-                               block:   nil,
+                               block:   mockBlock(),
                                tx:      tx,
                                entryID: tx.ID,
                                gas: &gasState{
-                                       gasLeft: uint64(1000),
+                                       gasLeft: int64(80000),
                                        gasUsed: 0,
                                },
                                cache: make(map[bc.Hash]error),
@@ -297,6 +297,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))
                        }
@@ -304,18 +305,6 @@ 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
-       })
-
-       _, err := ValidateTx(legacy.MapTx(&tx.TxData), nil)
-       if errors.Root(err) != bc.ErrMissingEntry {
-               t.Fatalf("got %s, want %s", err, bc.ErrMissingEntry)
-       }
-}
-
 func TestBlockHeaderValid(t *testing.T) {
        base := bc.NewBlockHeader(1, 1, &bc.Hash{}, 1, &bc.Hash{}, &bc.Hash{}, 0, 0)
        baseBytes, _ := proto.Marshal(base)
@@ -430,6 +419,9 @@ func sample(tb testing.TB, in *txFixture) *txFixture {
                        legacy.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2, *newHash(9), []byte{10}),
                }
        }
+
+       result.txInputs = append(result.txInputs, mockGasTxInput())
+
        if len(result.txOutputs) == 0 {
                cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
                if err != nil {
@@ -467,6 +459,18 @@ func sample(tb testing.TB, in *txFixture) *txFixture {
        return &result
 }
 
+func mockBlock() *bc.Block {
+       return &bc.Block{
+               BlockHeader: &bc.BlockHeader{
+                       Height: 666,
+               },
+       }
+}
+
+func mockGasTxInput() *legacy.TxInput {
+       return legacy.NewSpendInput([][]byte{}, *newHash(8), *validation.BTMAssetID, 100000000, 0, []byte{byte(vm.OP_TRUE)}, *newHash(9), []byte{})
+}
+
 // Like errors.Root, but also unwraps vm.Error objects.
 func rootErr(e error) error {
        for {
index 820c0be..096b477 100644 (file)
@@ -9,8 +9,10 @@ import (
        "github.com/bytom/protocol/vm"
 )
 
-func NewTxVMContext(tx *bc.Tx, entry bc.Entry, prog *bc.Program, args [][]byte) *vm.Context {
+func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args [][]byte) *vm.Context {
        var (
+               tx         = vs.tx
+               blockHeigh = vs.block.BlockHeader.GetHeight()
                numResults = uint64(len(tx.ResultIds))
                txData     = tx.Data.Bytes()
                entryID    = bc.EntryID(entry) // TODO(bobg): pass this in, don't recompute it
@@ -91,7 +93,8 @@ func NewTxVMContext(tx *bc.Tx, entry bc.Entry, prog *bc.Program, args [][]byte)
 
                EntryID: entryID.Bytes(),
 
-               TxVersion: &tx.Version,
+               TxVersion:  &tx.Version,
+               BlockHeigh: &blockHeigh,
 
                TxSigHash:     txSigHashFn,
                NumResults:    &numResults,