)
const (
- defaultGasLimit = uint64(80000)
- gasRate = uint64(1000)
+ defaultGasLimit = int64(80000)
+ gasRate = int64(1000)
)
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
}
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")
}
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 {
}
}
- 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
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
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) {
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 {
}
}
- 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)
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
}
}
// 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,
cache: make(map[bc.Hash]error),
}
- return &vs.gas.BTMValue, checkValid(vs, tx.TxHeader)
+ err := checkValid(vs, tx.TxHeader)
+ return vs.gas.BTMValue, err
}
"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"
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),
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
- })
-
- _, 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)
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 {
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 {