internal/ethapi, accounts/abi/bind: cap highest gas limit by account balance for 1559 fee parameters (#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>
This commit is contained in:
lightclient 2021-08-10 07:56:34 -06:00 committed by GitHub
parent 39fe7eca6b
commit a879c42bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 7 deletions

View File

@ -488,8 +488,19 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} else { } else {
hi = b.pendingBlock.GasLimit() 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. // 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 balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance) available := new(big.Int).Set(balance)
if call.Value != nil { if call.Value != nil {
@ -498,14 +509,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} }
available.Sub(available, call.Value) 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() { if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value transfer := call.Value
if transfer == nil { if transfer == nil {
transfer = new(big.Int) transfer = new(big.Int)
} }
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, 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() hi = allowance.Uint64()
} }
} }

View File

@ -580,6 +580,26 @@ func TestEstimateGasWithPrice(t *testing.T) {
Value: big.NewInt(100000000000), Value: big.NewInt(100000000000),
Data: nil, Data: nil,
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14) }, 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 { for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message) got, err := sim.EstimateGas(context.Background(), c.message)
@ -592,6 +612,9 @@ func TestEstimateGasWithPrice(t *testing.T) {
} }
continue continue
} }
if c.expectError == nil && err != nil {
t.Fatalf("test %d: didn't expect error, got %v", i, err)
}
if got != c.expect { if got != c.expect {
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got) t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
} }

View File

@ -477,7 +477,7 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio
if args.Nonce == nil { if args.Nonce == nil {
return nil, fmt.Errorf("nonce not specified") 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() tx := args.toTransaction()
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err return nil, err
@ -1008,8 +1008,19 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
} }
hi = block.GasLimit() 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. // 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) state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if err != nil { if err != nil {
return 0, err return 0, err
@ -1022,7 +1033,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
} }
available.Sub(available, args.Value.ToInt()) 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 the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() { if allowance.IsUint64() && hi > allowance.Uint64() {
@ -1031,7 +1042,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
transfer = new(hexutil.Big) transfer = new(hexutil.Big)
} }
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, 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() hi = allowance.Uint64()
} }
} }