From 4089ea0695f384a8be4ccc8d8bd713c1a8736d43 Mon Sep 17 00:00:00 2001 From: Janet Liang <56005637+janet-harmony@users.noreply.github.com> Date: Tue, 15 Sep 2020 14:07:26 -0700 Subject: [PATCH] [pool] Update pool logic to accept Delegate transactions using redelegation tokens (#3344) * [config] Fix confusing variable name * [pool] Allow pool to accept Delegate transactions that utilize the locked redelegation amount * [pool] Bypass balance check for Delegate transactions, let validation be done with validateStakingTx * [list] Perform validation on staking transactions when filtering for overpriced/invalid transactions * [list] Rename FilterCost to FilterValid, to reflect changes & update comment * [pool] Remove now unnecessary staking logic in demotion logic * [list] Fix imports --- core/tx_list.go | 25 ++++++------- core/tx_pool.go | 51 ++++++++++++--------------- internal/configs/sharding/localnet.go | 6 ++-- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index b6c6e1ca5..72de4d21f 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -23,9 +23,9 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" - "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/internal/utils" + staking "github.com/harmony-one/harmony/staking/types" ) // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for @@ -283,16 +283,13 @@ func (l *txList) Forward(threshold uint64) types.PoolTransactions { return l.txs.Forward(threshold) } -// FilterCost removes all transactions from the list with a cost or gas limit higher -// than the provided thresholds. Every removed transaction is returned for any -// post-removal maintenance. Strict-mode invalidated transactions are also -// returned. -// -// This method uses the cached costcap and gascap to quickly decide if there's even -// a point in calculating all the costs or if the balance covers all. If the threshold -// is lower than the costgas cap, the caps will be reset to a new high after removing -// the newly invalidated transactions. -func (l *txList) FilterCost(costLimit *big.Int, gasLimit uint64) (types.PoolTransactions, types.PoolTransactions) { +// FilterValid returns all regular transactions from the list with a cost or gas limit higher +// than the provided thresholds and all staking transactions that can not be validated. +func (l *txList) FilterValid( + txPool *TxPool, address common.Address, +) (types.PoolTransactions, types.PoolTransactions) { + costLimit := txPool.currentState.GetBalance(address) + gasLimit := txPool.currentMaxGas // If all transactions are below the threshold, short circuit if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { return nil, nil @@ -305,7 +302,11 @@ func (l *txList) FilterCost(costLimit *big.Int, gasLimit uint64) (types.PoolTran if err != nil { return true // failure should lead to removal of the tx } - return cost.Cmp(costLimit) > 0 || tx.Gas() > gasLimit + if _, ok := tx.(*staking.StakingTransaction); ok { + err := txPool.validateTx(tx, false) + return err != nil + } + return cost.Cmp(costLimit) == 1 || tx.Gas() > gasLimit }) } diff --git a/core/tx_pool.go b/core/tx_pool.go index 3f897e5bd..8cca70ef1 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -728,15 +728,17 @@ func (pool *TxPool) validateTx(tx types.PoolTransaction, local bool) error { if err != nil { return err } - if pool.currentState.GetBalance(from).Cmp(cost) < 0 { - return errors.Wrapf( - ErrInsufficientFunds, - "current shard-id: %d", - pool.chain.CurrentBlock().ShardID(), - ) + stakingTx, isStakingTx := tx.(*staking.StakingTransaction) + if !isStakingTx || (isStakingTx && stakingTx.StakingType() != staking.DirectiveDelegate) { + if pool.currentState.GetBalance(from).Cmp(cost) < 0 { + return errors.Wrapf( + ErrInsufficientFunds, + "current shard-id: %d", + pool.chain.CurrentBlock().ShardID(), + ) + } } intrGas := uint64(0) - stakingTx, isStakingTx := tx.(*staking.StakingTransaction) if isStakingTx { intrGas, err = IntrinsicGas(tx.Data(), false, pool.homestead, stakingTx.StakingType() == staking.DirectiveCreateValidator) } else { @@ -832,10 +834,7 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { if err != nil { return err } - pendingEpoch := pool.chain.CurrentBlock().Epoch() - if shard.Schedule.IsLastBlock(pool.chain.CurrentBlock().Number().Uint64()) { - pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1)) - } + pendingEpoch := pool.pendingEpoch() _, _, _, err = VerifyAndDelegateFromMsg( pool.currentState, pendingEpoch, stkMsg, delegations, pool.chainconfig.IsRedelegation(pendingEpoch)) return err @@ -851,12 +850,8 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { if from != stkMsg.DelegatorAddress { return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32) } - pendingEpoch := pool.chain.CurrentBlock().Epoch() - if shard.Schedule.IsLastBlock(pool.chain.CurrentBlock().Number().Uint64()) { - pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1)) - } - _, err = VerifyAndUndelegateFromMsg(pool.currentState, pendingEpoch, stkMsg) + _, err = VerifyAndUndelegateFromMsg(pool.currentState, pool.pendingEpoch(), stkMsg) return err case staking.DirectiveCollectRewards: msg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCollectRewards) @@ -887,6 +882,15 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { } } +func (pool *TxPool) pendingEpoch() *big.Int { + currentBlock := pool.chain.CurrentBlock() + pendingEpoch := currentBlock.Epoch() + if shard.Schedule.IsLastBlock(currentBlock.Number().Uint64()) { + pendingEpoch.Add(pendingEpoch, big.NewInt(1)) + } + return pendingEpoch +} + // add validates a transaction and inserts it into the non-executable queue for // later pending promotion and execution. If the transaction is a replacement for // an already pending or queued one, it overwrites the previous and returns this @@ -1297,7 +1301,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) { // Do not report to error sink as old txs are on chain or meaningful error caught elsewhere. } // Drop all transactions that are too costly (low balance or out of gas) - drops, _ := list.FilterCost(pool.currentState.GetBalance(addr), pool.currentMaxGas) + drops, _ := list.FilterValid(pool, addr) for _, tx := range drops { hash := tx.Hash() pool.all.Remove(hash) @@ -1470,18 +1474,7 @@ func (pool *TxPool) demoteUnexecutables() { // Do not report to error sink as old txs are on chain or meaningful error caught elsewhere. } // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later - drops, invalids := list.FilterCost(pool.currentState.GetBalance(addr), pool.currentMaxGas) - // Drop all staking transactions that are now invalid, queue any invalids back for later - stakingDrops, stakingInvalids := list.Filter(func(tx types.PoolTransaction) bool { - if _, ok := tx.(*staking.StakingTransaction); !ok { - // Do not remove anything other than staking transactions - return false - } - err := pool.validateTx(tx, false) - return err != nil - }) - drops = append(drops, stakingDrops...) - invalids = append(invalids, stakingInvalids...) + drops, invalids := list.FilterValid(pool, addr) for _, tx := range drops { hash := tx.Hash() pool.all.Remove(hash) diff --git a/internal/configs/sharding/localnet.go b/internal/configs/sharding/localnet.go index 7fc2c9d40..65b3f1716 100644 --- a/internal/configs/sharding/localnet.go +++ b/internal/configs/sharding/localnet.go @@ -19,8 +19,8 @@ type localnetSchedule struct{} const ( localnetV1Epoch = 1 - localnetEpochBlock1 = 10 - twoOne = 5 + localnetEpochBlock1 = 10 + localnetBlocksPerEpoch = 5 localnetVdfDifficulty = 5000 // This takes about 10s to finish the vdf localnetConsensusRatio = float64(0.1) @@ -40,7 +40,7 @@ func (ls localnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { } func (ls localnetSchedule) BlocksPerEpoch() uint64 { - return twoOne + return localnetBlocksPerEpoch } func (ls localnetSchedule) CalcEpochNumber(blockNum uint64) *big.Int {