Skip to content

Commit

Permalink
internal/ethapi, accounts/abi/bind: cap highest gas limit by account …
Browse files Browse the repository at this point in the history
…balance for 1559 fee parameters (ethereum#23309)

* internal/ethapi/api: cap highest gas limit by account balance for 1559 fee parameters

* accounts/abi/bind: port gas limit cap for 1559 parameters to simulated backend

* accounts/abi/bind: add test for 1559 gas estimates for the simulated backend

* internal/ethapi/api: fix comment

* accounts/abi/bind/backends, internal/ethapi: unify naming style

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
  • Loading branch information
2 people authored and Martin committed Aug 28, 2021
1 parent 087464b commit 705ba4c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
17 changes: 14 additions & 3 deletions accounts/abi/bind/backends/simulated.go
Expand Up @@ -488,8 +488,19 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} else {
hi = b.pendingBlock.GasLimit()
}
// Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} else if call.GasPrice != nil {
feeCap = call.GasPrice
} else if call.GasFeeCap != nil {
feeCap = call.GasFeeCap
} else {
feeCap = common.Big0
}
// Recap the highest gas allowance with account's balance.
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
if feeCap.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance)
if call.Value != nil {
Expand All @@ -498,14 +509,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
}
available.Sub(available, call.Value)
}
allowance := new(big.Int).Div(available, call.GasPrice)
allowance := new(big.Int).Div(available, feeCap)
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
"sent", transfer, "feecap", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
Expand Down
23 changes: 23 additions & 0 deletions accounts/abi/bind/backends/simulated_test.go
Expand Up @@ -580,6 +580,26 @@ func TestEstimateGasWithPrice(t *testing.T) {
Value: big.NewInt(100000000000),
Data: nil,
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)

{"EstimateEIP1559WithHighFees", ethereum.CallMsg{
From: addr,
To: &addr,
Gas: 0,
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
GasTipCap: big.NewInt(1),
Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
Data: nil,
}, params.TxGas, nil},

{"EstimateEIP1559WithSuperHighFees", ethereum.CallMsg{
From: addr,
To: &addr,
Gas: 0,
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
GasTipCap: big.NewInt(1),
Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether
Data: nil,
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
}
for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message)
Expand All @@ -592,6 +612,9 @@ func TestEstimateGasWithPrice(t *testing.T) {
}
continue
}
if c.expectError == nil && err != nil {
t.Fatalf("test %d: didn't expect error, got %v", i, err)
}
if got != c.expect {
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
}
Expand Down
19 changes: 15 additions & 4 deletions internal/ethapi/api.go
Expand Up @@ -477,7 +477,7 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio
if args.Nonce == nil {
return nil, fmt.Errorf("nonce not specified")
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
// Before actually signing the transaction, ensure the transaction fee is reasonable.
tx := args.toTransaction()
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
Expand Down Expand Up @@ -1008,8 +1008,19 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
hi = block.GasLimit()
}
// Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} else if args.GasPrice != nil {
feeCap = args.GasPrice.ToInt()
} else if args.MaxFeePerGas != nil {
feeCap = args.MaxFeePerGas.ToInt()
} else {
feeCap = common.Big0
}
// Recap the highest gas limit with account's available balance.
if args.GasPrice != nil && args.GasPrice.ToInt().BitLen() != 0 {
if feeCap.BitLen() != 0 {
state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return 0, err
Expand All @@ -1022,7 +1033,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
available.Sub(available, args.Value.ToInt())
}
allowance := new(big.Int).Div(available, args.GasPrice.ToInt())
allowance := new(big.Int).Div(available, feeCap)

// If the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() {
Expand All @@ -1031,7 +1042,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
transfer = new(hexutil.Big)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer.ToInt(), "gasprice", args.GasPrice.ToInt(), "fundable", allowance)
"sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
Expand Down

0 comments on commit 705ba4c

Please sign in to comment.