Send dynamic fee tx parameters

This commit is contained in:
Andrea Maria Piana 2021-07-14 09:48:01 +02:00
parent 81b58b39ec
commit 03700c934c
4 changed files with 123 additions and 39 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/status-im/status-go/eth-node/types"
@ -60,10 +61,13 @@ func (w *rpcWrapper) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uin
// If the transaction was a contract creation use the TransactionReceipt method to get the
// contract address after the transaction has been mined.
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
log.Info("encoding transaction to bytes", "tx", tx)
data, err := rlp.EncodeToBytes(tx)
if err != nil {
log.Error("failed to encode transaction to bytes", "tx", tx, "err", err)
return err
}
log.Info("encoded transaction to bytes", "tx", tx)
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", types.EncodeHex(data))
}

View File

@ -264,7 +264,7 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
nonce = uint64(*args.Nonce)
}
gasPrice := (*big.Int)(args.GasPrice)
if args.GasPrice == nil {
if !args.IsDynamicFeeTx() && args.GasPrice == nil {
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel()
gasPrice, err = t.gasCalculator.SuggestGasPrice(ctx)
@ -277,7 +277,9 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
value := (*big.Int)(args.Value)
var gas uint64
if args.Gas == nil {
if args.Gas != nil {
gas = uint64(*args.Gas)
} else if args.Gas == nil && !args.IsDynamicFeeTx() {
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel()
@ -303,20 +305,20 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
gas = defaultGas
}
} else {
gas = uint64(*args.Gas)
}
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
if err != nil {
t.log.Info("ERROR SIGNIN TRANSACTION", "hash", hash)
return hash, err
}
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel()
if err := t.sender.SendTransaction(ctx, signedTx); err != nil {
t.log.Info("ERROR SENDING TRANSACTION", "hash", hash)
return hash, err
}
return types.Hash(signedTx.Hash()), nil
@ -335,7 +337,37 @@ func (t *Transactor) buildTransactionWithOverrides(nonce uint64, value *big.Int,
var tx *gethtypes.Transaction
if args.To != nil {
tx = gethtypes.NewTransaction(nonce, common.Address(*args.To), value, gas, gasPrice, args.GetInput())
to := common.Address(*args.To)
var txData gethtypes.TxData
t.log.Info("checking fee for dynamic", "args", args)
if args.IsDynamicFeeTx() {
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
txData = &gethtypes.DynamicFeeTx{
Nonce: nonce,
Gas: gas,
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
To: &to,
Value: value,
Data: args.GetInput(),
}
t.log.Info("is dynamic", "txdata", txData)
} else {
txData = &gethtypes.LegacyTx{
Nonce: nonce,
GasPrice: gasPrice,
Gas: gas,
To: &to,
Value: value,
Data: args.GetInput(),
}
t.log.Info("is not dynamic", "txdata", txData)
}
tx = gethtypes.NewTx(txData)
t.logNewTx(args, gas, gasPrice, value)
} else {
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())

View File

@ -80,17 +80,19 @@ func (s *TransactorSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce,
var usedGas hexutil.Uint64
var usedGasPrice *big.Int
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(account.Address)), gethrpc.PendingBlockNumber).Return(&returnNonce, nil)
if args.GasPrice == nil {
usedGasPrice = (*big.Int)(testGasPrice)
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil)
} else {
usedGasPrice = (*big.Int)(args.GasPrice)
}
if args.Gas == nil {
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil)
usedGas = testGas
} else {
usedGas = *args.Gas
if !args.IsDynamicFeeTx() {
if args.GasPrice == nil {
usedGasPrice = (*big.Int)(testGasPrice)
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil)
} else {
usedGasPrice = (*big.Int)(args.GasPrice)
}
if args.Gas == nil {
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil)
usedGas = testGas
} else {
usedGas = *args.Gas
}
}
// Prepare the transaction and RLP encode it.
data := s.rlpEncodeTx(args, s.nodeConfig, account, &resultNonce, usedGas, usedGasPrice)
@ -99,16 +101,35 @@ func (s *TransactorSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce,
}
func (s *TransactorSuite) rlpEncodeTx(args SendTxArgs, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes {
newTx := gethtypes.NewTransaction(
uint64(*nonce),
common.Address(*args.To),
args.Value.ToInt(),
uint64(gas),
gasPrice,
[]byte(args.Input),
)
var txData gethtypes.TxData
to := common.Address(*args.To)
if args.IsDynamicFeeTx() {
gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
gasFeeCap := (*big.Int)(args.MaxFeePerGas)
txData = &gethtypes.DynamicFeeTx{
Nonce: uint64(*nonce),
Gas: uint64(gas),
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
To: &to,
Value: args.Value.ToInt(),
Data: args.GetInput(),
}
} else {
txData = &gethtypes.LegacyTx{
Nonce: uint64(*nonce),
GasPrice: gasPrice,
Gas: uint64(gas),
To: &to,
Value: args.Value.ToInt(),
Data: args.GetInput(),
}
}
newTx := gethtypes.NewTx(txData)
chainID := big.NewInt(int64(config.NetworkID))
signedTx, err := gethtypes.SignTx(newTx, gethtypes.NewEIP155Signer(chainID), account.AccountKey.PrivateKey)
signedTx, err := gethtypes.SignTx(newTx, gethtypes.NewLondonSigner(chainID), account.AccountKey.PrivateKey)
s.NoError(err)
data, err := rlp.EncodeToBytes(signedTx)
s.NoError(err)
@ -122,29 +143,47 @@ func (s *TransactorSuite) TestGasValues() {
AccountKey: &types.Key{PrivateKey: key},
}
testCases := []struct {
name string
gas *hexutil.Uint64
gasPrice *hexutil.Big
name string
gas *hexutil.Uint64
gasPrice *hexutil.Big
maxFeePerGas *hexutil.Big
maxPriorityFeePerGas *hexutil.Big
}{
{
"noGasDef",
nil,
nil,
nil,
nil,
},
{
"gasDefined",
&testGas,
nil,
nil,
nil,
},
{
"gasPriceDefined",
nil,
testGasPrice,
nil,
nil,
},
{
"nilSignTransactionSpecificArgs",
nil,
nil,
nil,
nil,
},
{
"maxFeeAndPriorityset",
nil,
nil,
testGasPrice,
testGasPrice,
},
}
@ -152,10 +191,12 @@ func (s *TransactorSuite) TestGasValues() {
s.T().Run(testCase.name, func(t *testing.T) {
s.SetupTest()
args := SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
Gas: testCase.gas,
GasPrice: testCase.gasPrice,
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
Gas: testCase.gas,
GasPrice: testCase.gasPrice,
MaxFeePerGas: testCase.maxFeePerGas,
MaxPriorityFeePerGas: testCase.maxPriorityFeePerGas,
}
s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil)
@ -309,7 +350,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
}
// simulate transaction signed externally
signer := gethtypes.NewEIP155Signer(chainID)
signer := gethtypes.NewLondonSigner(chainID)
tx := gethtypes.NewTransaction(uint64(nonce), common.Address(to), (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
hash := signer.Hash(tx)
sig, err := gethcrypto.Sign(hash[:], privKey)

View File

@ -38,12 +38,14 @@ type GasCalculator interface {
// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom
// over the exact layout of this struct.
type SendTxArgs struct {
From types.Address `json:"from"`
To *types.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"`
From types.Address `json:"from"`
To *types.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
// We keep both "input" and "data" for backward compatibility.
// "input" is a preferred field.
// see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107`
@ -62,6 +64,11 @@ func (args SendTxArgs) Valid() bool {
return bytes.Equal(args.Input, args.Data)
}
// IsDynamicFeeTx checks whether dynamic fee parameters are set for the tx
func (args SendTxArgs) IsDynamicFeeTx() bool {
return args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
}
// GetInput returns either Input or Data field's value dependent on what is filled.
func (args SendTxArgs) GetInput() types.HexBytes {
if !isNilOrEmpty(args.Input) {