From bc15577e1fa4a3e9d82d30fe525b4de1d331f3fc Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 29 Jul 2021 14:00:06 +0200 Subject: [PATCH] accounts/external, internal/ethapi: fixes for London tx signing (#23274) Ticket #23273 found a flaw where we were unable to sign legacy-transactions using the external signer, even if we're still on non-london network. That's fixed in this PR. Additionally, I found that even when supplying all parameters, it was impossible to sign a london-transaction on an unsynched node. It's a pretty common usecase that someone wants to sign a transaction using an unsynced 'vanilla' node, providing all necessary data. Our setDefaults, however, insisted on checking the current block against the config. This PR therefore adds a case, so that if both MaxPriorityFeePerGas and MaxFeePerGas are provided, we accept them as given. OBS This PR fixes a regression -- on current master, we are unable to sign a london-transaction unless the node is synched, which may break scenarios where geth (or clef) is used as a cold wallet. Fixes #23273 --- accounts/external/backend.go | 9 ++-- internal/ethapi/transaction_args.go | 67 ++++++++++++++++------------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/accounts/external/backend.go b/accounts/external/backend.go index 4f6ca399673c3..f6715a84eb212 100644 --- a/accounts/external/backend.go +++ b/accounts/external/backend.go @@ -211,11 +211,14 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio To: to, From: common.NewMixedcaseAddress(account.Address), } - if tx.GasFeeCap() != nil { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + args.GasPrice = (*hexutil.Big)(tx.GasPrice()) + case types.DynamicFeeTxType: args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap()) args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap()) - } else { - args.GasPrice = (*hexutil.Big)(tx.GasPrice()) + default: + return nil, fmt.Errorf("Unsupported tx type %d", tx.Type()) } // We should request the default chain id that we're operating with // (the chain we're executing on) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 1fbaaeacbf064..9076e49b2a66a 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -80,40 +80,45 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // After london, default to 1559 unless gasPrice is set head := b.CurrentHeader() - if b.ChainConfig().IsLondon(head.Number) && args.GasPrice == nil { - if args.MaxPriorityFeePerGas == nil { - tip, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err + // If user specifies both maxPriorityfee and maxFee, then we do not + // need to consult the chain for defaults. It's definitely a London tx. + if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { + // In this clause, user left some fields unspecified. + if b.ChainConfig().IsLondon(head.Number) && args.GasPrice == nil { + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) } - args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) - } - if args.MaxFeePerGas == nil { - gasFeeCap := new(big.Int).Add( - (*big.Int)(args.MaxPriorityFeePerGas), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) - } - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } - } else { - if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") - } - if args.GasPrice == nil { - price, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err + if args.MaxFeePerGas == nil { + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) + } + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + } else { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { + return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } - if b.ChainConfig().IsLondon(head.Number) { - // The legacy tx gas price suggestion should not add 2x base fee - // because all fees are consumed, so it would result in a spiral - // upwards. - price.Add(price, head.BaseFee) + if args.GasPrice == nil { + price, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + if b.ChainConfig().IsLondon(head.Number) { + // The legacy tx gas price suggestion should not add 2x base fee + // because all fees are consumed, so it would result in a spiral + // upwards. + price.Add(price, head.BaseFee) + } + args.GasPrice = (*hexutil.Big)(price) } - args.GasPrice = (*hexutil.Big)(price) } } if args.Value == nil {