Skip to content

Commit

Permalink
Validate debitGasFees execution in tx pool
Browse files Browse the repository at this point in the history
Txs must not fail during debitGasFees execution during the state
transition, so we must remove problematic txs during the tx pool
validation.
  • Loading branch information
karlb committed Mar 4, 2024
1 parent b5848a7 commit 2cbd161
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 34 deletions.
33 changes: 26 additions & 7 deletions contracts/erc20gas/erc20gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/common/hexutil"
"github.com/celo-org/celo-blockchain/contracts/internal/n"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/celo-org/celo-blockchain/core/vm"
"github.com/celo-org/celo-blockchain/log"
)
Expand All @@ -17,17 +18,35 @@ const (
maxGasForCreditGasFeesTransactions uint64 = 1 * n.Million
)

var (
// Recalculate with `cast sig 'debitGasFees(address from, uint256 value)'`
debitGasFeesSelector = hexutil.MustDecode("0x58cf9672")
)

// Returns nil if debit is possible, used in tx pool validation
func TryDebitFees(tx *types.Transaction, from common.Address, currentVMRunner vm.EVMRunner) error {
cost := new(big.Int).SetUint64(tx.Gas())
cost.Mul(cost, tx.GasFeeCap())

// The following code is similar to DebitFees, but that function does not work on a vm.EVMRunner,
// so we have to adapt it instead of reusing.
transactionData := common.GetEncodedAbi(debitGasFeesSelector, [][]byte{common.AddressToAbi(from), common.AmountToAbi(cost)})

ret, err := currentVMRunner.Query(*tx.FeeCurrency(), transactionData, maxGasForDebitGasFeesTransactions)
if err != nil {
revertReason, err2 := abi.UnpackRevert(ret)
if err2 == nil {
return fmt.Errorf("TryDebitFees reverted: %s", revertReason)
}
}
return nil
}

func DebitFees(evm *vm.EVM, address common.Address, amount *big.Int, feeCurrency *common.Address) error {
if amount.Cmp(big.NewInt(0)) == 0 {
return nil
}
// Function is "debitGasFees(address from, uint256 value)"
// selector is first 4 bytes of keccak256 of "debitGasFees(address,uint256)"
// Source:
// pip3 install pyethereum
// python3 -c 'from ethereum.utils import sha3; print(sha3("debitGasFees(address,uint256)")[0:4].hex())'
functionSelector := hexutil.MustDecode("0x58cf9672")
transactionData := common.GetEncodedAbi(functionSelector, [][]byte{common.AddressToAbi(address), common.AmountToAbi(amount)})
transactionData := common.GetEncodedAbi(debitGasFeesSelector, [][]byte{common.AddressToAbi(address), common.AmountToAbi(amount)})

// Run only primary evm.Call() with tracer
if evm.GetDebug() {
Expand Down
30 changes: 3 additions & 27 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/celo-org/celo-blockchain/consensus"
"github.com/celo-org/celo-blockchain/contracts/blockchain_parameters"
"github.com/celo-org/celo-blockchain/contracts/currency"
"github.com/celo-org/celo-blockchain/contracts/erc20gas"
gpm "github.com/celo-org/celo-blockchain/contracts/gasprice_minimum"
"github.com/celo-org/celo-blockchain/core/state"
"github.com/celo-org/celo-blockchain/core/types"
Expand Down Expand Up @@ -1721,8 +1722,7 @@ func (pool *TxPool) demoteUnexecutables() {
// - Post-Espresso: it ensures balance >= GasFeeCap * gas + value + gatewayFee (2)
//
// For non-native tokens(cUSD, cEUR, ...) as feeCurrency:
// - Pre-Espresso: it ensures balance > GasPrice * gas + gatewayFee (3)
// - Post-Espresso: it ensures balance >= GasFeeCap * gas + gatewayFee (4)
// - It executes a static call on debitGasFees, implicitly ensuring balance >= GasFeeCap * gas and that `from` is not on the token's block list
func ValidateTransactorBalanceCoversTx(tx *types.Transaction, from common.Address, currentState *state.StateDB, currentVMRunner vm.EVMRunner, espresso bool) error {
if tx.FeeCurrency() == nil {
balance := currentState.GetBalance(from)
Expand All @@ -1749,31 +1749,7 @@ func ValidateTransactorBalanceCoversTx(tx *types.Transaction, from common.Addres
return ErrInsufficientFunds
}
} else {
balance, err := currency.GetBalanceOf(currentVMRunner, from, *tx.FeeCurrency())
if err != nil {
log.Debug("ValidateTransactorBalanceCoversTx: error in getting fee currency balance", "feeCurrency", tx.FeeCurrency())
return err
}

if espresso {
// cost = GasFeeCap * gas + gatewayFee, as in (4)
cost := new(big.Int).SetUint64(tx.Gas())
cost.Mul(cost, tx.GasFeeCap())
if tx.GatewayFeeRecipient() != nil {
cost.Add(cost, tx.GatewayFee())
}
if balance.Cmp(cost) < 0 {
log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance)
return ErrInsufficientFunds
}
} else {
// cost = GasPrice * gas + gatewayFee, as in (3)
cost := tx.Fee()
if balance.Cmp(cost) <= 0 {
log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance)
return ErrInsufficientFunds
}
}
return erc20gas.TryDebitFees(tx, from, currentVMRunner)
}

return nil
Expand Down

0 comments on commit 2cbd161

Please sign in to comment.