"fmt"
"math"
- "github.com/vapor/claim"
"github.com/vapor/consensus"
"github.com/vapor/consensus/segwit"
"github.com/vapor/errors"
"github.com/vapor/protocol/vm"
)
+const ruleAA = 142500
+
// validate transaction error
var (
ErrTxVersion = errors.New("invalid transaction version")
ErrWrongTransactionSize = errors.New("invalid transaction size")
ErrBadTimeRange = errors.New("invalid transaction time range")
+ ErrEmptyInputIDs = errors.New("got the empty InputIDs")
ErrNotStandardTx = errors.New("not standard transaction")
ErrWrongCoinbaseTransaction = errors.New("wrong coinbase transaction")
ErrWrongCoinbaseAsset = errors.New("wrong coinbase assetID")
case *bc.Mux:
parity := make(map[bc.AssetID]int64)
for i, src := range e.Sources {
- _, ok := vs.tx.Entries[*src.Ref]
- if !ok {
- return errors.Wrapf(bc.ErrMissingEntry, "entry for bytom input %x not found", *src.Ref)
- }
-
if src.Value.Amount > math.MaxInt64 {
return errors.WithDetailf(ErrOverflow, "amount %d exceeds maximum value 2^63", src.Value.Amount)
}
if !ok {
return errors.Wrapf(bc.ErrMissingEntry, "entry for bytom input %x not found", BTMInputID)
}
+
vs2 := *vs
vs2.entryID = BTMInputID
if err := checkValid(&vs2, e); err != nil {
}
}
- if len(vs.tx.GasInputIDs) > 0 {
- if err := vs.gasStatus.setGasValid(); err != nil {
- return err
- }
+ if err := vs.gasStatus.setGasValid(); err != nil {
+ return err
}
for i, src := range e.Sources {
}
}
- case *bc.Output:
+ case *bc.IntraChainOutput:
+ vs2 := *vs
+ vs2.sourcePos = 0
+ if err = checkValidSrc(&vs2, e.Source); err != nil {
+ return errors.Wrap(err, "checking output source")
+ }
+
+ case *bc.CrossChainOutput:
vs2 := *vs
vs2.sourcePos = 0
if err = checkValidSrc(&vs2, e.Source); err != nil {
if e.SpentOutputId == nil {
return errors.Wrap(ErrMissingField, "spend without spent output ID")
}
- spentOutput, err := vs.tx.Output(*e.SpentOutputId)
+ spentOutput, err := vs.tx.IntraChainOutput(*e.SpentOutputId)
if err != nil {
return errors.Wrap(err, "getting spend prevout")
}
+
gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
if err != nil {
return errors.Wrap(err, "checking control program")
if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
return errors.Wrap(err, "checking coinbase destination")
}
+ vs.gasStatus.StorageGas = 0
- // special case for coinbase transaction, it's valid unit all the verify has been passed
- vs.gasStatus.GasValid = true
- case *bc.Claim:
- // 对交易的合法性进行验证
- if e.SpentOutputId == nil {
- return errors.Wrap(ErrMissingField, "spend without spent output ID")
- }
- spentOutput, err := vs.tx.Output(*e.SpentOutputId)
- if err != nil {
- return errors.Wrap(err, "getting spend prevout")
- }
- stack := e.GetPeginwitness()
- if len(stack) < 5 || stack[1] == nil || spentOutput.Source == nil {
-
- return errors.New("pegin-no-witness")
- }
-
- // 根据claim链类型选择验证类型
- validation := &claim.BytomClaimValidation{}
- if err := validation.IsValidPeginWitness(stack, *spentOutput); err != nil {
- return err
- }
-
- eq, err := spentOutput.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, spend wants %d unit(s) of %x",
- spentOutput.Source.Value.Amount,
- spentOutput.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 spend destination")
- }
- vs.gasStatus.GasValid = true
default:
return fmt.Errorf("entry has unexpected type %T", e)
}
return errors.Wrapf(ErrPosition, "invalid position %d for %d-destination mux source", vs.Position, len(ref.WitnessDestinations))
}
dest = ref.WitnessDestinations[vs.Position]
- case *bc.Claim:
- if vs.Position != 0 {
- return errors.Wrapf(ErrPosition, "invalid position %d for coinbase source", vs.Position)
- }
- dest = ref.WitnessDestination
+
default:
return errors.Wrapf(bc.ErrEntryType, "value source is %T, should be coinbase, issuance, spend, or mux", e)
}
return errors.Wrap(ErrMissingField, "missing ref on value destination")
}
if vd.Value == nil || vd.Value.AssetId == nil {
- return errors.Wrap(ErrMissingField, "missing value on value source")
+ return errors.Wrap(ErrMissingField, "missing value on value destination")
}
e, ok := vs.tx.Entries[*vd.Ref]
var src *bc.ValueSource
switch ref := e.(type) {
- case *bc.Output:
+ case *bc.IntraChainOutput:
+ if vd.Position != 0 {
+ return errors.Wrapf(ErrPosition, "invalid position %d for output destination", vd.Position)
+ }
+ src = ref.Source
+
+ case *bc.CrossChainOutput:
if vd.Position != 0 {
return errors.Wrapf(ErrPosition, "invalid position %d for output destination", vd.Position)
}
src = ref.Sources[vd.Position]
default:
- return errors.Wrapf(bc.ErrEntryType, "value destination is %T, should be output, retirement, or mux", e)
+ return errors.Wrapf(bc.ErrEntryType, "value destination is %T, should be intra-chain/cross-chain output, retirement, or mux", e)
}
if src.Ref == nil || *src.Ref != vs.entryID {
return nil
}
-func checkStandardTx(tx *bc.Tx) error {
+func checkStandardTx(tx *bc.Tx, blockHeight uint64) error {
+ for _, id := range tx.InputIDs {
+ if blockHeight >= ruleAA && id.IsZero() {
+ return ErrEmptyInputIDs
+ }
+ }
+
for _, id := range tx.GasInputIDs {
spend, err := tx.Spend(id)
if err != nil {
- return err
+ continue
}
- spentOutput, err := tx.Output(*spend.SpentOutputId)
+
+ intraChainSpentOutput, err := tx.IntraChainOutput(*spend.SpentOutputId)
if err != nil {
return err
}
- if !segwit.IsP2WScript(spentOutput.ControlProgram.Code) {
+ if !segwit.IsP2WScript(intraChainSpentOutput.ControlProgram.Code) {
return ErrNotStandardTx
}
}
return errors.Wrapf(bc.ErrMissingEntry, "id %x", id.Bytes())
}
- output, ok := e.(*bc.Output)
- if !ok || *output.Source.Value.AssetId != *consensus.BTMAssetID {
+ var prog []byte
+ switch e := e.(type) {
+ case *bc.IntraChainOutput:
+ if *e.Source.Value.AssetId != *consensus.BTMAssetID {
+ continue
+ }
+ prog = e.ControlProgram.Code
+
+ case *bc.CrossChainOutput:
+ if *e.Source.Value.AssetId != *consensus.BTMAssetID {
+ continue
+ }
+ prog = e.ControlProgram.Code
+
+ default:
continue
}
- if !segwit.IsP2WScript(output.ControlProgram.Code) {
+ if !segwit.IsP2WScript(prog) {
return ErrNotStandardTx
}
}
+
return nil
}
if tx.TimeRange < block.Height {
return ErrBadTimeRange
}
+
return nil
}
if err := checkTimeRange(tx, block); err != nil {
return gasStatus, err
}
- if err := checkStandardTx(tx); err != nil {
+ if err := checkStandardTx(tx, block.Height); err != nil {
return gasStatus, err
}
+
vs := &validationState{
block: block,
tx: tx,