X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=blobdiff_plain;f=protocol%2Fvalidation%2Ftx.go;h=64fbde959d9e9b3c0615f380857dc7fcd2ba5dc6;hp=8d36ea5340d7e5bf1941d7f00e1d6ef706503b63;hb=b62a559ff00dffbffe1e2b87288e91f8908e6d04;hpb=db158dcf09436b003defd333f1a665e7e051d820 diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index 8d36ea53..64fbde95 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -1,9 +1,11 @@ package validation import ( + "bytes" "fmt" "math" + "github.com/vapor/config" "github.com/vapor/consensus" "github.com/vapor/consensus/segwit" "github.com/vapor/errors" @@ -36,6 +38,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 @@ -210,50 +214,81 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } } - 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.Retirement: + case *bc.CrossChainOutput: vs2 := *vs vs2.sourcePos = 0 if err = checkValidSrc(&vs2, e.Source); err != nil { - return errors.Wrap(err, "checking retirement source") + return errors.Wrap(err, "checking output source") } - case *bc.Issuance: - computedAssetID := e.WitnessAssetDefinition.ComputeAssetID() - if computedAssetID != *e.Value.AssetId { - return errors.WithDetailf(ErrMismatchedAssetID, "asset ID is %x, issuance wants %x", computedAssetID.Bytes(), e.Value.AssetId.Bytes()) + 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 } - gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), vs.gasStatus.GasLeft) - if err != nil { - return errors.Wrap(err, "checking issuance program") + case *bc.Retirement: + vs2 := *vs + vs2.sourcePos = 0 + if err = checkValidSrc(&vs2, e.Source); err != nil { + return errors.Wrap(err, "checking retirement source") } - if err = vs.gasStatus.updateUsage(gasLeft); err != nil { - return err + + case *bc.CrossChainInput: + _, err := vm.Verify(NewTxVMContext(vs, e, e.ControlProgram, e.WitnessArguments), consensus.DefaultGasCredit) + if err != nil { + return errors.Wrap(err, "checking cross-chain input control program") } - destVS := *vs - destVS.destPos = 0 - if err = checkValidDest(&destVS, e.WitnessDestination); err != nil { - return errors.Wrap(err, "checking issuance destination") + vs2 := *vs + vs2.destPos = 0 + 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") } - spentOutput, err := vs.tx.Output(*e.SpentOutputId) + var ( + controlProgram *bc.Program + value *bc.AssetAmount + ) + entryOutput, err := vs.tx.Entry(*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) + 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) if err != nil { return errors.Wrap(err, "checking control program") } @@ -261,7 +296,7 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { return err } - eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value) + eq, err := value.Equal(e.WitnessDestination.Value) if err != nil { return err } @@ -269,13 +304,12 @@ 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", - spentOutput.Source.Value.Amount, - spentOutput.Source.Value.AssetId.Bytes(), + value.Amount, + 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 { @@ -339,9 +373,9 @@ func checkValidSrc(vstate *validationState, vs *bc.ValueSource) error { } dest = ref.WitnessDestination - case *bc.Issuance: + case *bc.CrossChainInput: if vs.Position != 0 { - return errors.Wrapf(ErrPosition, "invalid position %d for issuance source", vs.Position) + return errors.Wrapf(ErrPosition, "invalid position %d for cross-chain input source", vs.Position) } dest = ref.WitnessDestination @@ -358,7 +392,7 @@ func checkValidSrc(vstate *validationState, vs *bc.ValueSource) error { dest = ref.WitnessDestinations[vs.Position] default: - return errors.Wrapf(bc.ErrEntryType, "value source is %T, should be coinbase, issuance, spend, or mux", e) + return errors.Wrapf(bc.ErrEntryType, "value source is %T, should be coinbase, cross-chain input, spend, or mux", e) } if dest.Ref == nil || *dest.Ref != vstate.entryID { @@ -398,7 +432,19 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { 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.Source + + case *bc.VoteOutput: if vd.Position != 0 { return errors.Wrapf(ErrPosition, "invalid position %d for output destination", vd.Position) } @@ -417,7 +463,7 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { 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 { @@ -439,6 +485,21 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { 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 { for _, id := range tx.InputIDs { if blockHeight >= ruleAA && id.IsZero() { @@ -446,33 +507,31 @@ func checkStandardTx(tx *bc.Tx, blockHeight uint64) error { } } + if err := checkFedaration(tx); err != nil { + return err + } + for _, id := range tx.GasInputIDs { spend, err := tx.Spend(id) if err != nil { continue } - spentOutput, err := tx.Output(*spend.SpentOutputId) + + code := []byte{} + outputEntry, err := tx.Entry(*spend.SpentOutputId) if err != nil { return err } - - if !segwit.IsP2WScript(spentOutput.ControlProgram.Code) { - return ErrNotStandardTx - } - } - - for _, id := range tx.ResultIds { - e, ok := tx.Entries[*id] - if !ok { - return errors.Wrapf(bc.ErrMissingEntry, "id %x", id.Bytes()) + 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) } - output, ok := e.(*bc.Output) - if !ok || *output.Source.Value.AssetId != *consensus.BTMAssetID { - continue - } - - if !segwit.IsP2WScript(output.ControlProgram.Code) { + if !segwit.IsP2WScript(code) { return ErrNotStandardTx } } @@ -487,6 +546,7 @@ func checkTimeRange(tx *bc.Tx, block *bc.Block) error { if tx.TimeRange < block.Height { return ErrBadTimeRange } + return nil }