[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"
"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
})
}

@ -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)

@ -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 {

Loading…
Cancel
Save