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"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types" gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/status-im/status-go/eth-node/types" "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 // If the transaction was a contract creation use the TransactionReceipt method to get the
// contract address after the transaction has been mined. // contract address after the transaction has been mined.
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error { func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
log.Info("encoding transaction to bytes", "tx", tx)
data, err := rlp.EncodeToBytes(tx) data, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
log.Error("failed to encode transaction to bytes", "tx", tx, "err", err)
return err return err
} }
log.Info("encoded transaction to bytes", "tx", tx)
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", types.EncodeHex(data)) 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) nonce = uint64(*args.Nonce)
} }
gasPrice := (*big.Int)(args.GasPrice) gasPrice := (*big.Int)(args.GasPrice)
if args.GasPrice == nil { if !args.IsDynamicFeeTx() && args.GasPrice == nil {
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout) ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel() defer cancel()
gasPrice, err = t.gasCalculator.SuggestGasPrice(ctx) gasPrice, err = t.gasCalculator.SuggestGasPrice(ctx)
@ -277,7 +277,9 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
value := (*big.Int)(args.Value) value := (*big.Int)(args.Value)
var gas uint64 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) ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel() 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) t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
gas = defaultGas gas = defaultGas
} }
} else {
gas = uint64(*args.Gas)
} }
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args) tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey) signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
if err != nil { if err != nil {
t.log.Info("ERROR SIGNIN TRANSACTION", "hash", hash)
return hash, err return hash, err
} }
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout) ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
defer cancel() defer cancel()
if err := t.sender.SendTransaction(ctx, signedTx); err != nil { if err := t.sender.SendTransaction(ctx, signedTx); err != nil {
t.log.Info("ERROR SENDING TRANSACTION", "hash", hash)
return hash, err return hash, err
} }
return types.Hash(signedTx.Hash()), nil return types.Hash(signedTx.Hash()), nil
@ -335,7 +337,37 @@ func (t *Transactor) buildTransactionWithOverrides(nonce uint64, value *big.Int,
var tx *gethtypes.Transaction var tx *gethtypes.Transaction
if args.To != nil { 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) t.logNewTx(args, gas, gasPrice, value)
} else { } else {
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput()) 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 usedGas hexutil.Uint64
var usedGasPrice *big.Int var usedGasPrice *big.Int
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(account.Address)), gethrpc.PendingBlockNumber).Return(&returnNonce, nil) s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(account.Address)), gethrpc.PendingBlockNumber).Return(&returnNonce, nil)
if args.GasPrice == nil { if !args.IsDynamicFeeTx() {
usedGasPrice = (*big.Int)(testGasPrice) if args.GasPrice == nil {
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil) usedGasPrice = (*big.Int)(testGasPrice)
} else { s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(testGasPrice, nil)
usedGasPrice = (*big.Int)(args.GasPrice) } else {
} usedGasPrice = (*big.Int)(args.GasPrice)
if args.Gas == nil { }
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil) if args.Gas == nil {
usedGas = testGas s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(testGas, nil)
} else { usedGas = testGas
usedGas = *args.Gas } else {
usedGas = *args.Gas
}
} }
// Prepare the transaction and RLP encode it. // Prepare the transaction and RLP encode it.
data := s.rlpEncodeTx(args, s.nodeConfig, account, &resultNonce, usedGas, usedGasPrice) 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 { 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( var txData gethtypes.TxData
uint64(*nonce), to := common.Address(*args.To)
common.Address(*args.To), if args.IsDynamicFeeTx() {
args.Value.ToInt(), gasTipCap := (*big.Int)(args.MaxPriorityFeePerGas)
uint64(gas), gasFeeCap := (*big.Int)(args.MaxFeePerGas)
gasPrice,
[]byte(args.Input), 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)) 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) s.NoError(err)
data, err := rlp.EncodeToBytes(signedTx) data, err := rlp.EncodeToBytes(signedTx)
s.NoError(err) s.NoError(err)
@ -122,29 +143,47 @@ func (s *TransactorSuite) TestGasValues() {
AccountKey: &types.Key{PrivateKey: key}, AccountKey: &types.Key{PrivateKey: key},
} }
testCases := []struct { testCases := []struct {
name string name string
gas *hexutil.Uint64 gas *hexutil.Uint64
gasPrice *hexutil.Big gasPrice *hexutil.Big
maxFeePerGas *hexutil.Big
maxPriorityFeePerGas *hexutil.Big
}{ }{
{ {
"noGasDef", "noGasDef",
nil, nil,
nil, nil,
nil,
nil,
}, },
{ {
"gasDefined", "gasDefined",
&testGas, &testGas,
nil, nil,
nil,
nil,
}, },
{ {
"gasPriceDefined", "gasPriceDefined",
nil, nil,
testGasPrice, testGasPrice,
nil,
nil,
}, },
{ {
"nilSignTransactionSpecificArgs", "nilSignTransactionSpecificArgs",
nil, nil,
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.T().Run(testCase.name, func(t *testing.T) {
s.SetupTest() s.SetupTest()
args := SendTxArgs{ args := SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress), From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress), To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
Gas: testCase.gas, Gas: testCase.gas,
GasPrice: testCase.gasPrice, GasPrice: testCase.gasPrice,
MaxFeePerGas: testCase.maxFeePerGas,
MaxPriorityFeePerGas: testCase.maxPriorityFeePerGas,
} }
s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil) s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil)
@ -309,7 +350,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
} }
// simulate transaction signed externally // 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) tx := gethtypes.NewTransaction(uint64(nonce), common.Address(to), (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
hash := signer.Hash(tx) hash := signer.Hash(tx)
sig, err := gethcrypto.Sign(hash[:], privKey) 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 // 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. // over the exact layout of this struct.
type SendTxArgs struct { type SendTxArgs struct {
From types.Address `json:"from"` From types.Address `json:"from"`
To *types.Address `json:"to"` To *types.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"` Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"` GasPrice *hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"` Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"` Nonce *hexutil.Uint64 `json:"nonce"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
// We keep both "input" and "data" for backward compatibility. // We keep both "input" and "data" for backward compatibility.
// "input" is a preferred field. // "input" is a preferred field.
// see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107` // 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) 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. // GetInput returns either Input or Data field's value dependent on what is filled.
func (args SendTxArgs) GetInput() types.HexBytes { func (args SendTxArgs) GetInput() types.HexBytes {
if !isNilOrEmpty(args.Input) { if !isNilOrEmpty(args.Input) {