Skip to content

Commit

Permalink
core: add new eip-1559 tx constraints (ethereum#22970)
Browse files Browse the repository at this point in the history
This PR adds the new consensus constraints of EIP-1559 transactions as specified in https://github.com/ethereum/EIPs#3594
  • Loading branch information
zsfelfoldi authored May 30, 2021
1 parent 966ee3a commit 2d716c4
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 12 deletions.
12 changes: 12 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ var (
// current network configuration.
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported

// ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
// transaction with a tip higher than the total fee cap.
ErrTipAboveFeeCap = errors.New("tip higher than fee cap")

// ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
// in the tip field.
ErrTipVeryHigh = errors.New("tip higher than 2^256-1")

// ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified
// in the fee cap field.
ErrFeeCapVeryHigh = errors.New("fee cap higher than 2^256-1")

// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
// the base fee of the block.
ErrFeeCapTooLow = errors.New("fee cap less than block base fee")
Expand Down
49 changes: 43 additions & 6 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestStateProcessorErrors(t *testing.T) {
Nonce: nonce,
Tip: tip,
FeeCap: feeCap,
Gas: 0,
Gas: gasLimit,
To: &to,
Value: big.NewInt(0),
}), signer, testKey)
Expand All @@ -88,7 +88,9 @@ func TestStateProcessorErrors(t *testing.T) {
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
)
defer blockchain.Stop()

bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
tooBigNumber := new(big.Int).Set(bigNumber)
tooBigNumber.Add(tooBigNumber, common.Big1)
for i, tt := range []struct {
txs []*types.Transaction
want string
Expand Down Expand Up @@ -142,11 +144,46 @@ func TestStateProcessorErrors(t *testing.T) {
},
{ // ErrFeeCapTooLow
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)),
},
want: "could not apply tx 0 [0x21e9b9015150fc7f6bd5059890a5e1727f2452df285e8a84f4ca61a74c159ded]: fee cap less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, feeCap: 0 baseFee: 875000000",
want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: fee cap less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, feeCap: 0 baseFee: 875000000",
},
} {
{ // ErrTipVeryHigh
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)),
},
want: "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: tip higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tip bit length: 257",
},
{ // ErrFeeCapVeryHigh
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber),
},
want: "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: fee cap higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, feeCap bit length: 257",
},
{ // ErrTipAboveFeeCap
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)),
},
want: "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: tip higher than fee cap: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tip: 1, feeCap: 2",
},
{ // ErrInsufficientFunds
// Available balance: 1000000000000000000
// Effective cost: 18375000021000
// FeeCap * gas: 1050000000000000000
// This test is designed to have the effective cost be covered by the balance, but
// the extended requirement on FeeCap*gas < balance to fail
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)),
},
want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000",
},
{ // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
},
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
},
}[8:] {
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
_, err := blockchain.InsertChain(types.Blocks{block})
if err == nil {
Expand Down Expand Up @@ -194,7 +231,7 @@ func TestStateProcessorErrors(t *testing.T) {
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
},
want: "could not apply tx 0 [0x21e9b9015150fc7f6bd5059890a5e1727f2452df285e8a84f4ca61a74c159ded]: transaction type not supported",
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
},
} {
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
Expand Down
22 changes: 20 additions & 2 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,14 @@ func (st *StateTransition) to() common.Address {
}

func (st *StateTransition) buyGas() error {
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if have, want := st.state.GetBalance(st.msg.From()), mgval; have.Cmp(want) < 0 {
mgval := new(big.Int).SetUint64(st.msg.Gas())
mgval = mgval.Mul(mgval, st.gasPrice)
balanceCheck := mgval
if st.feeCap != nil {
balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
balanceCheck = balanceCheck.Mul(balanceCheck, st.feeCap)
}
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
Expand All @@ -215,6 +221,18 @@ func (st *StateTransition) preCheck() error {
}
// Make sure that transaction feeCap is greater than the baseFee (post london)
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
if l := st.feeCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, feeCap bit length: %d", ErrFeeCapVeryHigh,
st.msg.From().Hex(), l)
}
if l := st.tip.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, tip bit length: %d", ErrTipVeryHigh,
st.msg.From().Hex(), l)
}
if st.feeCap.Cmp(st.tip) < 0 {
return fmt.Errorf("%w: address %v, tip: %s, feeCap: %s", ErrTipAboveFeeCap,
st.msg.From().Hex(), st.feeCap, st.tip)
}
// This will panic if baseFee is nil, but basefee presence is verified
// as part of header validation.
if st.feeCap.Cmp(st.evm.Context.BaseFee) < 0 {
Expand Down
11 changes: 7 additions & 4 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ var (
// than some meaningful limit a user might use. This is not a consensus error
// making the transaction invalid, rather a DOS protection.
ErrOversizedData = errors.New("oversized data")

// ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
// transaction with a tip higher than the total fee cap.
ErrTipAboveFeeCap = errors.New("tip higher than fee cap")
)

var (
Expand Down Expand Up @@ -559,6 +555,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
// Sanity check for extremely large numbers
if tx.FeeCap().BitLen() > 256 {
return ErrFeeCapVeryHigh
}
if tx.Tip().BitLen() > 256 {
return ErrTipVeryHigh
}
// Ensure feeCap is less than or equal to tip.
if tx.FeeCapIntCmp(tx.Tip()) < 0 {
return ErrTipAboveFeeCap
Expand Down
20 changes: 20 additions & 0 deletions core/tx_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,26 @@ func TestTransactionTipAboveFeeCap(t *testing.T) {
}
}

func TestTransactionVeryHighValues(t *testing.T) {
t.Parallel()

pool, key := setupTxPoolWithConfig(eip1559Config)
defer pool.Stop()

veryBigNumber := big.NewInt(1)
veryBigNumber.Lsh(veryBigNumber, 300)

tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key)
if err := pool.AddRemote(tx); err != ErrTipVeryHigh {
t.Error("expected", ErrTipVeryHigh, "got", err)
}

tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key)
if err := pool.AddRemote(tx2); err != ErrFeeCapVeryHigh {
t.Error("expected", ErrFeeCapVeryHigh, "got", err)
}
}

func TestTransactionChainFork(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 2d716c4

Please sign in to comment.