X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fvalidation%2Ftx.go;h=6d248e543563f3e1ff9e7b78ef2374a45041f017;hb=f30b7541256e6a7f6f875994cb08edf3bddab93c;hp=a7488555f14595f9b62572aca2c36528c88a5ac3;hpb=e34f38162f5d016893e53d6f14d47ed06dafe031;p=bytom%2Fvapor.git diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index a7488555..6d248e54 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -3,16 +3,20 @@ package validation import ( "fmt" "math" + "sync" + "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" "github.com/vapor/protocol/vm" ) -const ruleAA = 142500 +const ( + validateWorkerNum = 32 + ruleAA = 142500 +) // validate transaction error var ( @@ -36,6 +40,8 @@ var ( ErrUnbalanced = errors.New("unbalanced asset amount between input and output") 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 @@ -225,11 +231,17 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } case *bc.VoteOutput: + if len(e.Vote) != 64 { + return ErrVotePubKey + } vs2 := *vs vs2.sourcePos = 0 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 @@ -239,8 +251,27 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } case *bc.CrossChainInput: - _, err := vm.Verify(NewTxVMContext(vs, e, e.ControlProgram, e.WitnessArguments), consensus.DefaultGasCredit) + if e.MainchainOutputId == nil { + return errors.Wrap(ErrMissingField, "crosschain input without mainchain output ID") + } + + mainchainOutput, err := vs.tx.IntraChainOutput(*e.MainchainOutputId) if err != nil { + return errors.Wrap(err, "getting mainchain output") + } + + assetID := e.AssetDefinition.ComputeAssetID() + if *mainchainOutput.Source.Value.AssetId != *consensus.BTMAssetID && *mainchainOutput.Source.Value.AssetId != assetID { + return errors.New("incorrect asset_id while checking CrossChainInput") + } + + code := config.FederationWScript(config.CommonConfig) + prog := &bc.Program{ + VmVersion: e.ControlProgram.VmVersion, + Code: code, + } + + if _, err := vm.Verify(NewTxVMContext(vs, e, prog, e.WitnessArguments), consensus.DefaultGasCredit); err != nil { return errors.Wrap(err, "checking cross-chain input control program") } @@ -249,32 +280,19 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { 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: - 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") } @@ -282,7 +300,7 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { return err } - eq, err := value.Equal(e.WitnessDestination.Value) + eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value) if err != nil { return err } @@ -290,8 +308,8 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { 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(), ) @@ -302,6 +320,47 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { 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 @@ -353,6 +412,12 @@ func checkValidSrc(vstate *validationState, vs *bc.ValueSource) error { 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) @@ -471,37 +536,12 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { 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 } } - - 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 } @@ -529,7 +569,7 @@ func ValidateTx(tx *bc.Tx, block *bc.Block) (*GasState, error) { 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 } @@ -542,3 +582,67 @@ func ValidateTx(tx *bc.Tx, block *bc.Block) (*GasState, error) { } return vs.gasStatus, checkValid(vs, tx.TxHeader) } + +type validateTxWork struct { + i int + tx *bc.Tx + block *bc.Block +} + +type validateTxResult struct { + i int + gasStatus *GasState + err error +} + +func (r *validateTxResult) GetGasState() *GasState { + return r.gasStatus +} + +func (r *validateTxResult) GetError() error { + return r.err +} + +func validateTxWorker(workCh chan *validateTxWork, resultCh chan *validateTxResult, closeCh chan struct{}, wg *sync.WaitGroup) { + for { + select { + case work := <-workCh: + gasStatus, err := ValidateTx(work.tx, work.block) + resultCh <- &validateTxResult{i: work.i, gasStatus: gasStatus, err: err} + case <-closeCh: + wg.Done() + return + } + } +} + +func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*validateTxResult { + txSize := len(txs) + //init the goroutine validate worker + var wg sync.WaitGroup + workCh := make(chan *validateTxWork, txSize) + resultCh := make(chan *validateTxResult, txSize) + closeCh := make(chan struct{}) + for i := 0; i <= validateWorkerNum && i < txSize; i++ { + wg.Add(1) + go validateTxWorker(workCh, resultCh, closeCh, &wg) + } + + //sent the works + for i, tx := range txs { + workCh <- &validateTxWork{i: i, tx: tx, block: block} + } + + //collect validate results + results := make([]*validateTxResult, txSize) + for i := 0; i < txSize; i++ { + result := <-resultCh + results[result.i] = result + } + + close(closeCh) + wg.Wait() + close(workCh) + close(resultCh) + return results +}