[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
pull/3350/head
Janet Liang 4 years ago committed by GitHub
parent f9c26e663b
commit 4089ea0695
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      core/tx_list.go
  2. 51
      core/tx_pool.go
  3. 6
      internal/configs/sharding/localnet.go

@ -23,9 +23,9 @@ import (
"sort" "sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/utils" "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 // 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) return l.txs.Forward(threshold)
} }
// FilterCost removes all transactions from the list with a cost or gas limit higher // FilterValid returns all regular transactions from the list with a cost or gas limit higher
// than the provided thresholds. Every removed transaction is returned for any // than the provided thresholds and all staking transactions that can not be validated.
// post-removal maintenance. Strict-mode invalidated transactions are also func (l *txList) FilterValid(
// returned. txPool *TxPool, address common.Address,
// ) (types.PoolTransactions, types.PoolTransactions) {
// This method uses the cached costcap and gascap to quickly decide if there's even costLimit := txPool.currentState.GetBalance(address)
// a point in calculating all the costs or if the balance covers all. If the threshold gasLimit := txPool.currentMaxGas
// 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) {
// If all transactions are below the threshold, short circuit // If all transactions are below the threshold, short circuit
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
return nil, nil return nil, nil
@ -305,7 +302,11 @@ func (l *txList) FilterCost(costLimit *big.Int, gasLimit uint64) (types.PoolTran
if err != nil { if err != nil {
return true // failure should lead to removal of the tx 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
}) })
} }

@ -728,15 +728,17 @@ func (pool *TxPool) validateTx(tx types.PoolTransaction, local bool) error {
if err != nil { if err != nil {
return err return err
} }
if pool.currentState.GetBalance(from).Cmp(cost) < 0 { stakingTx, isStakingTx := tx.(*staking.StakingTransaction)
return errors.Wrapf( if !isStakingTx || (isStakingTx && stakingTx.StakingType() != staking.DirectiveDelegate) {
ErrInsufficientFunds, if pool.currentState.GetBalance(from).Cmp(cost) < 0 {
"current shard-id: %d", return errors.Wrapf(
pool.chain.CurrentBlock().ShardID(), ErrInsufficientFunds,
) "current shard-id: %d",
pool.chain.CurrentBlock().ShardID(),
)
}
} }
intrGas := uint64(0) intrGas := uint64(0)
stakingTx, isStakingTx := tx.(*staking.StakingTransaction)
if isStakingTx { if isStakingTx {
intrGas, err = IntrinsicGas(tx.Data(), false, pool.homestead, stakingTx.StakingType() == staking.DirectiveCreateValidator) intrGas, err = IntrinsicGas(tx.Data(), false, pool.homestead, stakingTx.StakingType() == staking.DirectiveCreateValidator)
} else { } else {
@ -832,10 +834,7 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error {
if err != nil { if err != nil {
return err return err
} }
pendingEpoch := pool.chain.CurrentBlock().Epoch() pendingEpoch := pool.pendingEpoch()
if shard.Schedule.IsLastBlock(pool.chain.CurrentBlock().Number().Uint64()) {
pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1))
}
_, _, _, err = VerifyAndDelegateFromMsg( _, _, _, err = VerifyAndDelegateFromMsg(
pool.currentState, pendingEpoch, stkMsg, delegations, pool.chainconfig.IsRedelegation(pendingEpoch)) pool.currentState, pendingEpoch, stkMsg, delegations, pool.chainconfig.IsRedelegation(pendingEpoch))
return err return err
@ -851,12 +850,8 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error {
if from != stkMsg.DelegatorAddress { if from != stkMsg.DelegatorAddress {
return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32) 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 return err
case staking.DirectiveCollectRewards: case staking.DirectiveCollectRewards:
msg, err := staking.RLPDecodeStakeMsg(tx.Data(), 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 // 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 // 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 // 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. // 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) // 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 { for _, tx := range drops {
hash := tx.Hash() hash := tx.Hash()
pool.all.Remove(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. // 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 // 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) drops, invalids := list.FilterValid(pool, addr)
// 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...)
for _, tx := range drops { for _, tx := range drops {
hash := tx.Hash() hash := tx.Hash()
pool.all.Remove(hash) pool.all.Remove(hash)

@ -19,8 +19,8 @@ type localnetSchedule struct{}
const ( const (
localnetV1Epoch = 1 localnetV1Epoch = 1
localnetEpochBlock1 = 10 localnetEpochBlock1 = 10
twoOne = 5 localnetBlocksPerEpoch = 5
localnetVdfDifficulty = 5000 // This takes about 10s to finish the vdf localnetVdfDifficulty = 5000 // This takes about 10s to finish the vdf
localnetConsensusRatio = float64(0.1) localnetConsensusRatio = float64(0.1)
@ -40,7 +40,7 @@ func (ls localnetSchedule) InstanceForEpoch(epoch *big.Int) Instance {
} }
func (ls localnetSchedule) BlocksPerEpoch() uint64 { func (ls localnetSchedule) BlocksPerEpoch() uint64 {
return twoOne return localnetBlocksPerEpoch
} }
func (ls localnetSchedule) CalcEpochNumber(blockNum uint64) *big.Int { func (ls localnetSchedule) CalcEpochNumber(blockNum uint64) *big.Int {

Loading…
Cancel
Save