package validation
import (
- "bytes"
"fmt"
"math"
"github.com/vapor/config"
"github.com/vapor/consensus"
- "github.com/vapor/consensus/segwit"
"github.com/vapor/errors"
"github.com/vapor/math/checked"
"github.com/vapor/protocol/bc"
ErrOverGasCredit = errors.New("all gas credit has been spend")
ErrGasCalculate = errors.New("gas usage calculate got a math error")
ErrVotePubKey = errors.New("invalid public key of vote")
+ ErrVoteOutputAmount = errors.New("invalid vote amount")
)
// GasState record the gas usage status
if err = checkValidSrc(&vs2, e.Source); err != nil {
return errors.Wrap(err, "checking vote output source")
}
+ if e.Source.Value.Amount < consensus.MinVoteOutputAmount {
+ return ErrVoteOutputAmount
+ }
case *bc.Retirement:
vs2 := *vs
}
case *bc.CrossChainInput:
- _, err := vm.Verify(NewTxVMContext(vs, e, e.ControlProgram, e.WitnessArguments), consensus.DefaultGasCredit)
+ // check assetID
+ assetID := e.AssetDefinition.ComputeAssetID()
+ if *e.Value.AssetId != *consensus.BTMAssetID && *e.Value.AssetId != assetID {
+ return errors.New("incorrect asset_id while checking CrossChainInput")
+ }
+
+ code := config.FederationProgrom(config.CommonConfig)
+ prog := &bc.Program{
+ VmVersion: e.ControlProgram.VmVersion,
+ Code: code,
+ }
+ _, err := vm.Verify(NewTxVMContext(vs, e, prog, e.WitnessArguments), consensus.DefaultGasCredit)
if err != nil {
return errors.Wrap(err, "checking cross-chain input control program")
}
if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
return errors.Wrap(err, "checking cross-chain input destination")
}
+ vs.gasStatus.StorageGas = 0
case *bc.Spend:
if e.SpentOutputId == nil {
return errors.Wrap(ErrMissingField, "spend without spent output ID")
}
- var (
- controlProgram *bc.Program
- value *bc.AssetAmount
- )
- entryOutput, err := vs.tx.Entry(*e.SpentOutputId)
+
+ spentOutput, err := vs.tx.IntraChainOutput(*e.SpentOutputId)
if err != nil {
return errors.Wrap(err, "getting spend prevout")
}
- switch output := entryOutput.(type) {
- case *bc.IntraChainOutput:
- controlProgram = output.ControlProgram
- value = output.Source.Value
- case *bc.VoteOutput:
- if len(output.Vote) != 64 {
- return ErrVotePubKey
- }
- controlProgram = output.ControlProgram
- value = output.Source.Value
- default:
- return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", e.SpentOutputId.Bytes(), entryOutput)
- }
-
- gasLeft, err := vm.Verify(NewTxVMContext(vs, e, controlProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
+ gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
if err != nil {
return errors.Wrap(err, "checking control program")
}
return err
}
- eq, err := value.Equal(e.WitnessDestination.Value)
+ eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value)
if err != nil {
return err
}
return errors.WithDetailf(
ErrMismatchedValue,
"previous output is for %d unit(s) of %x, spend wants %d unit(s) of %x",
- value.Amount,
- value.AssetId.Bytes(),
+ spentOutput.Source.Value.Amount,
+ spentOutput.Source.Value.AssetId.Bytes(),
e.WitnessDestination.Value.Amount,
e.WitnessDestination.Value.AssetId.Bytes(),
)
return errors.Wrap(err, "checking spend destination")
}
+ case *bc.VetoInput:
+ if e.SpentOutputId == nil {
+ return errors.Wrap(ErrMissingField, "vetoInput without vetoInput output ID")
+ }
+
+ voteOutput, err := vs.tx.VoteOutput(*e.SpentOutputId)
+ if err != nil {
+ return errors.Wrap(err, "getting vetoInput prevout")
+ }
+ if len(voteOutput.Vote) != 64 {
+ return ErrVotePubKey
+ }
+
+ gasLeft, err := vm.Verify(NewTxVMContext(vs, e, voteOutput.ControlProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
+ if err != nil {
+ return errors.Wrap(err, "checking control program")
+ }
+ if err = vs.gasStatus.updateUsage(gasLeft); err != nil {
+ return err
+ }
+
+ eq, err := voteOutput.Source.Value.Equal(e.WitnessDestination.Value)
+ if err != nil {
+ return err
+ }
+ if !eq {
+ return errors.WithDetailf(
+ ErrMismatchedValue,
+ "previous output is for %d unit(s) of %x, vetoInput wants %d unit(s) of %x",
+ voteOutput.Source.Value.Amount,
+ voteOutput.Source.Value.AssetId.Bytes(),
+ e.WitnessDestination.Value.Amount,
+ e.WitnessDestination.Value.AssetId.Bytes(),
+ )
+ }
+ vs2 := *vs
+ vs2.destPos = 0
+ if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
+ return errors.Wrap(err, "checking vetoInput destination")
+ }
+
case *bc.Coinbase:
if vs.block == nil || len(vs.block.Transactions) == 0 || vs.block.Transactions[0] != vs.tx {
return ErrWrongCoinbaseTransaction
var dest *bc.ValueDestination
switch ref := e.(type) {
+ case *bc.VetoInput:
+ if vs.Position != 0 {
+ return errors.Wrapf(ErrPosition, "invalid position %d for veto-input source", vs.Position)
+ }
+ dest = ref.WitnessDestination
+
case *bc.Coinbase:
if vs.Position != 0 {
return errors.Wrapf(ErrPosition, "invalid position %d for coinbase source", vs.Position)
return nil
}
-func checkFedaration(tx *bc.Tx) error {
- for _, id := range tx.InputIDs {
- switch inp := tx.Entries[id].(type) {
- case *bc.CrossChainInput:
- fedProg := config.FederationProgrom(config.CommonConfig)
- if !bytes.Equal(inp.ControlProgram.Code, fedProg) {
- return errors.New("The federal controlProgram is incorrect")
- }
- default:
- continue
- }
- }
- return nil
-}
-
-func checkStandardTx(tx *bc.Tx, blockHeight uint64) error {
+func checkInputID(tx *bc.Tx, blockHeight uint64) error {
for _, id := range tx.InputIDs {
if blockHeight >= ruleAA && id.IsZero() {
return ErrEmptyInputIDs
}
}
-
- if err := checkFedaration(tx); err != nil {
- return err
- }
-
- for _, id := range tx.GasInputIDs {
- spend, err := tx.Spend(id)
- if err != nil {
- continue
- }
-
- code := []byte{}
- outputEntry, err := tx.Entry(*spend.SpentOutputId)
- if err != nil {
- return err
- }
- switch output := outputEntry.(type) {
- case *bc.IntraChainOutput:
- code = output.ControlProgram.Code
- case *bc.VoteOutput:
- code = output.ControlProgram.Code
- default:
- return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", id.Bytes(), outputEntry)
- }
-
- if !segwit.IsP2WScript(code) {
- return ErrNotStandardTx
- }
- }
return nil
}
if err := checkTimeRange(tx, block); err != nil {
return gasStatus, err
}
- if err := checkStandardTx(tx, block.Height); err != nil {
+ if err := checkInputID(tx, block.Height); err != nil {
return gasStatus, err
}