From 8c593c45a93743970476772e9bdf36740585ddfd Mon Sep 17 00:00:00 2001 From: oysheng <33340252+oysheng@users.noreply.github.com> Date: Wed, 17 Jul 2019 09:47:22 +0800 Subject: [PATCH] modify utxo pending number and consensus node vote restrict (#285) * modify utxo pending number * add consensus node vote restrict * fix test * modify validate vote tx * modify config * unit test * add error * optimise * optimise * optimise * optimise config * apply Tx * delete * optimise --- api/errors.go | 37 ++++++++++++--------- blockchain/txbuilder/finalize.go | 9 +++--- consensus/general.go | 8 ++--- proposal/proposal.go | 15 ++++++--- protocol/state/consensus_result.go | 66 +++++++++++++++++++++++--------------- protocol/state/utxo_view_test.go | 4 +-- protocol/tx.go | 13 ++++++-- protocol/validation/tx.go | 3 -- wallet/utxo_test.go | 4 +-- 9 files changed, 95 insertions(+), 64 deletions(-) diff --git a/api/errors.go b/api/errors.go index 5391d070..5f8fe2fb 100644 --- a/api/errors.go +++ b/api/errors.go @@ -12,6 +12,7 @@ import ( "github.com/vapor/errors" "github.com/vapor/net/http/httperror" "github.com/vapor/net/http/httpjson" + "github.com/vapor/protocol" "github.com/vapor/protocol/validation" "github.com/vapor/protocol/vm" ) @@ -56,22 +57,25 @@ var respErrFormatter = map[error]httperror.Info{ // Transaction error namespace (7xx) // Build transaction error namespace (70x ~ 72x) - account.ErrInsufficient: {400, "BTM700", "Funds of account are insufficient"}, - account.ErrImmature: {400, "BTM701", "Available funds of account are immature"}, - account.ErrReserved: {400, "BTM702", "Available UTXOs of account have been reserved"}, - account.ErrMatchUTXO: {400, "BTM703", "UTXO with given hash not found"}, - ErrBadActionType: {400, "BTM704", "Invalid action type"}, - ErrBadAction: {400, "BTM705", "Invalid action object"}, - ErrBadActionConstruction: {400, "BTM706", "Invalid action construction"}, - txbuilder.ErrMissingFields: {400, "BTM707", "One or more fields are missing"}, - txbuilder.ErrBadAmount: {400, "BTM708", "Invalid asset amount"}, - account.ErrFindAccount: {400, "BTM709", "Account not found"}, - asset.ErrFindAsset: {400, "BTM710", "Asset not found"}, - txbuilder.ErrBadContractArgType: {400, "BTM711", "Invalid contract argument type"}, - txbuilder.ErrOrphanTx: {400, "BTM712", "Transaction input UTXO not found"}, - txbuilder.ErrExtTxFee: {400, "BTM713", "Transaction fee exceeded max limit"}, - txbuilder.ErrNoGasInput: {400, "BTM714", "Transaction has no gas input"}, - account.ErrVoteLock: {400, "BTM715", "Locked by the vote"}, + account.ErrInsufficient: {400, "BTM700", "Funds of account are insufficient"}, + account.ErrImmature: {400, "BTM701", "Available funds of account are immature"}, + account.ErrReserved: {400, "BTM702", "Available UTXOs of account have been reserved"}, + account.ErrMatchUTXO: {400, "BTM703", "UTXO with given hash not found"}, + account.ErrVoteLock: {400, "BTM704", "Locked by the vote"}, + account.ErrFindAccount: {400, "BTM705", "Account not found"}, + asset.ErrFindAsset: {400, "BTM706", "Asset not found"}, + + ErrBadActionType: {400, "BTM710", "Invalid action type"}, + ErrBadAction: {400, "BTM711", "Invalid action object"}, + ErrBadActionConstruction: {400, "BTM712", "Invalid action construction"}, + txbuilder.ErrMissingFields: {400, "BTM713", "One or more fields are missing"}, + txbuilder.ErrBadAmount: {400, "BTM714", "Invalid asset amount"}, + txbuilder.ErrBadContractArgType: {400, "BTM715", "Invalid contract argument type"}, + txbuilder.ErrOrphanTx: {400, "BTM716", "Transaction input UTXO not found"}, + txbuilder.ErrExtTxFee: {400, "BTM717", "Transaction fee exceeded max limit"}, + txbuilder.ErrNoGasInput: {400, "BTM718", "Transaction has no gas input"}, + txbuilder.ErrRejected: {400, "BTM719", "Transaction rejected"}, + protocol.ErrDustTx: {400, "BTM720", "Dust Transaction"}, // Submit transaction error namespace (73x ~ 79x) // Validation error (73x ~ 75x) @@ -94,6 +98,7 @@ var respErrFormatter = map[error]httperror.Info{ validation.ErrUnbalanced: {400, "BTM746", "Unbalanced asset amount between input and output"}, validation.ErrOverGasCredit: {400, "BTM747", "Gas credit has been spent"}, validation.ErrGasCalculate: {400, "BTM748", "Gas usage calculate got a math error"}, + validation.ErrVoteOutputAmount: {400, "BTM749", "Invalid vote amount"}, // VM error (76x ~ 78x) vm.ErrAltStackUnderflow: {400, "BTM760", "Alt stack underflow"}, diff --git a/blockchain/txbuilder/finalize.go b/blockchain/txbuilder/finalize.go index 577830fe..270bb3b8 100644 --- a/blockchain/txbuilder/finalize.go +++ b/blockchain/txbuilder/finalize.go @@ -55,12 +55,13 @@ func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error { tx.Tx.SerializedSize = uint64(len(data) / 2) isOrphan, err := c.ValidateTx(tx) - if errors.Root(err) == protocol.ErrBadTx { - return errors.Sub(ErrRejected, err) - } if err != nil { - return errors.WithDetail(err, "tx rejected: "+err.Error()) + if errors.Root(err) == err { + return errors.Sub(ErrRejected, err) + } + return err } + if isOrphan { return ErrOrphanTx } diff --git a/consensus/general.go b/consensus/general.go index fe8e7b4d..bc311b25 100644 --- a/consensus/general.go +++ b/consensus/general.go @@ -136,8 +136,8 @@ var MainNetParams = Params{ DefaultGasCredit: int64(160000), StorageGasRate: int64(1), VMGasRate: int64(200), - VotePendingBlockNumber: uint64(10000), - CoinbasePendingBlockNumber: uint64(100), + VotePendingBlockNumber: uint64(3456000), + CoinbasePendingBlockNumber: uint64(7200), CoinbaseArbitrarySizeLimit: 128, }, DPOSConfig: VaporDPOSConfig(), @@ -160,7 +160,7 @@ var TestNetParams = Params{ StorageGasRate: int64(1), VMGasRate: int64(200), VotePendingBlockNumber: uint64(10000), - CoinbasePendingBlockNumber: uint64(100), + CoinbasePendingBlockNumber: uint64(1200), CoinbaseArbitrarySizeLimit: 128, }, DPOSConfig: VaporDPOSConfig(), @@ -182,7 +182,7 @@ var SoloNetParams = Params{ StorageGasRate: int64(1), VMGasRate: int64(200), VotePendingBlockNumber: uint64(10000), - CoinbasePendingBlockNumber: uint64(100), + CoinbasePendingBlockNumber: uint64(1200), CoinbaseArbitrarySizeLimit: 128, }, DPOSConfig: VaporDPOSConfig(), diff --git a/proposal/proposal.go b/proposal/proposal.go index a4c51e43..35c021f0 100644 --- a/proposal/proposal.go +++ b/proposal/proposal.go @@ -108,6 +108,11 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager txs := txPool.GetTransactions() sort.Sort(byTime(txs)) + consensusResult, err := c.GetConsensusResultByHash(&preBlockHash) + if err != nil { + return nil, err + } + entriesTxs := []*bc.Tx{} for _, txDesc := range txs { entriesTxs = append(entriesTxs, txDesc.Tx.Tx) @@ -142,6 +147,11 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager continue } + if err := consensusResult.ApplyTransaction(txDesc.Tx); err != nil { + blkGenSkipTxForErr(txPool, &tx.ID, err) + continue + } + if err := txStatus.SetStatus(len(b.Transactions), gasOnlyTx); err != nil { return nil, err } @@ -161,11 +171,6 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager return nil, errors.Wrap(err, "fail on createCoinbaseTx") } - consensusResult, err := c.GetConsensusResultByHash(&preBlockHash) - if err != nil { - return nil, err - } - if err := consensusResult.AttachCoinbaseReward(b); err != nil { return nil, err } diff --git a/protocol/state/consensus_result.go b/protocol/state/consensus_result.go index e1241434..ff4a2a3b 100644 --- a/protocol/state/consensus_result.go +++ b/protocol/state/consensus_result.go @@ -109,39 +109,55 @@ func (c *ConsensusResult) ApplyBlock(block *types.Block) error { } for _, tx := range block.Transactions { - for _, input := range tx.Inputs { - vetoInput, ok := input.TypedInput.(*types.VetoInput) - if !ok { - continue - } + if err := c.ApplyTransaction(tx); err != nil { + return err + } + } - pubkey := hex.EncodeToString(vetoInput.Vote) - c.NumOfVote[pubkey], ok = checked.SubUint64(c.NumOfVote[pubkey], vetoInput.Amount) - if !ok { - return checked.ErrOverflow - } + c.BlockHash = block.Hash() + c.BlockHeight = block.Height + c.Seq = CalcVoteSeq(block.Height) + return nil +} - if c.NumOfVote[pubkey] == 0 { - delete(c.NumOfVote, pubkey) - } +// ApplyTransaction calculate the consensus result for transaction +func (c *ConsensusResult) ApplyTransaction(tx *types.Tx) error { + for _, input := range tx.Inputs { + vetoInput, ok := input.TypedInput.(*types.VetoInput) + if !ok { + continue } - for _, output := range tx.Outputs { - voteOutput, ok := output.TypedOutput.(*types.VoteOutput) - if !ok { - continue - } + pubkey := hex.EncodeToString(vetoInput.Vote) + c.NumOfVote[pubkey], ok = checked.SubUint64(c.NumOfVote[pubkey], vetoInput.Amount) + if !ok { + return checked.ErrOverflow + } - pubkey := hex.EncodeToString(voteOutput.Vote) - if c.NumOfVote[pubkey], ok = checked.AddUint64(c.NumOfVote[pubkey], voteOutput.Amount); !ok { - return checked.ErrOverflow - } + if c.NumOfVote[pubkey] == 0 { + delete(c.NumOfVote, pubkey) } } - c.BlockHash = block.Hash() - c.BlockHeight = block.Height - c.Seq = CalcVoteSeq(block.Height) + for _, output := range tx.Outputs { + voteOutput, ok := output.TypedOutput.(*types.VoteOutput) + if !ok { + continue + } + + if voteOutput.Amount < consensus.ActiveNetParams.MinVoteOutputAmount { + return errors.New("invalid vote transaction with vote amount less than MinVoteOutputAmount") + } + + pubkey := hex.EncodeToString(voteOutput.Vote) + if _, ok := c.NumOfVote[pubkey]; !ok && voteOutput.Amount < consensus.ActiveNetParams.MinConsensusNodeVoteNum { + return errors.New("invalid vote transaction with first vote amount less than MinConsensusNodeVoteNum") + } + + if c.NumOfVote[pubkey], ok = checked.AddUint64(c.NumOfVote[pubkey], voteOutput.Amount); !ok { + return checked.ErrOverflow + } + } return nil } diff --git a/protocol/state/utxo_view_test.go b/protocol/state/utxo_view_test.go index a41285c8..3cd4f4a9 100644 --- a/protocol/state/utxo_view_test.go +++ b/protocol/state/utxo_view_test.go @@ -166,7 +166,7 @@ func TestApplyBlock(t *testing.T) { { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ - Height: 101, + Height: consensus.MainNetParams.CoinbasePendingBlockNumber + 1, TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ @@ -340,7 +340,7 @@ func TestApplyBlock(t *testing.T) { { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ - Height: 10001, + Height: consensus.MainNetParams.VotePendingBlockNumber + 1, TransactionStatus: bc.NewTransactionStatus(), }, Transactions: []*bc.Tx{ diff --git a/protocol/tx.go b/protocol/tx.go index 966b292f..f24f95a6 100644 --- a/protocol/tx.go +++ b/protocol/tx.go @@ -10,9 +10,6 @@ import ( "github.com/vapor/protocol/validation" ) -// ErrBadTx is returned for transactions failing validation -var ErrBadTx = errors.New("invalid transaction") - // GetTransactionStatus return the transaction status of give block func (c *Chain) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) { return c.store.GetTransactionStatus(hash) @@ -48,6 +45,16 @@ func (c *Chain) validateTx(tx *types.Tx) (bool, error) { } bh := c.BestBlockHeader() + blockHash := bh.Hash() + consensusResult, err := c.GetConsensusResultByHash(&blockHash) + if err != nil { + return false, err + } + + if err := consensusResult.ApplyTransaction(tx); err != nil { + return false, errors.Wrap(validation.ErrVoteOutputAmount, err) + } + gasStatus, err := validation.ValidateTx(tx.Tx, types.MapBlock(&types.Block{BlockHeader: *bh})) if !gasStatus.GasValid { c.txPool.AddErrCache(&tx.ID, err) diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index d52c1055..93644552 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -238,9 +238,6 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { if err = checkValidSrc(&vs2, e.Source); err != nil { return errors.Wrap(err, "checking vote output source") } - if e.Source.Value.Amount < consensus.ActiveNetParams.MinVoteOutputAmount { - return ErrVoteOutputAmount - } case *bc.Retirement: vs2 := *vs diff --git a/wallet/utxo_test.go b/wallet/utxo_test.go index 2df108c3..9588a040 100644 --- a/wallet/utxo_test.go +++ b/wallet/utxo_test.go @@ -554,7 +554,7 @@ func TestTxOutToUtxos(t *testing.T) { ControlProgram: []byte{0x51}, SourceID: bc.NewHash([32]byte{0xb4, 0x7e, 0x94, 0x31, 0x88, 0xfe, 0xd3, 0xe9, 0xac, 0x99, 0x7c, 0xfc, 0x99, 0x6d, 0xd7, 0x4d, 0x04, 0x10, 0x77, 0xcb, 0x1c, 0xf8, 0x95, 0x14, 0x00, 0xe3, 0x42, 0x00, 0x8d, 0x05, 0xec, 0xdc}), SourcePos: 0, - ValidHeight: 198, + ValidHeight: consensus.MainNetParams.CoinbasePendingBlockNumber + 98, }, }, }, @@ -745,7 +745,7 @@ func TestTxOutToUtxos(t *testing.T) { ControlProgram: []byte{0x52}, SourceID: bc.Hash{V0: 14680680172533616824, V1: 32429899179491316, V2: 15399988966960786775, V3: 17411722803888206567}, SourcePos: 1, - ValidHeight: 100, + ValidHeight: consensus.MainNetParams.CoinbasePendingBlockNumber, }, }, }, -- 2.11.0