Skip to content

Commit

Permalink
Set defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
goran-ethernal committed Dec 28, 2023
1 parent 7e1a11b commit 86faf3f
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 55 deletions.
9 changes: 9 additions & 0 deletions helper/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ func BigMin(x, y *big.Int) *big.Int {
return x
}

// BigMax returns the larger of x or y.
func BigMax(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 {
return x
}

return y
}

func ConvertUnmarshalledUint(x interface{}) (uint64, error) {
switch tx := x.(type) {
case float64:
Expand Down
20 changes: 10 additions & 10 deletions jsonrpc/debug_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,16 +593,16 @@ func TestTraceCall(t *testing.T) {
blockNumber = BlockNumber(testBlock10.Number())

txArg = &txnArgs{
From: &from,
To: &to,
Gas: &gas,
GasPrice: &gasPrice,
GasTipCap: &gasTipCap,
GasFeeCap: &gasFeeCap,
Value: &value,
Data: &data,
Input: &input,
Nonce: &nonce,
From: &from,
To: &to,
Gas: &gas,
GasPrice: &gasPrice,
MaxPriorityFeePerGas: &gasTipCap,
MaxFeePerGas: &gasFeeCap,
Value: &value,
Data: &data,
Input: &input,
Nonce: &nonce,
}
decodedTx = &types.Transaction{
Nonce: uint64(nonce),
Expand Down
4 changes: 4 additions & 0 deletions jsonrpc/eth_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ func (e *Eth) SendRawTransaction(buf argBytes) (interface{}, error) {

// SendTransaction rejects eth_sendTransaction json-rpc call as we don't support wallet management
func (e *Eth) SendTransaction(args *txnArgs) (interface{}, error) {
if err := args.setDefaults(e.priceLimit, e); err != nil {
return nil, err
}

tx, err := DecodeTxn(args, e.store, true)
if err != nil {
return nil, err
Expand Down
38 changes: 19 additions & 19 deletions jsonrpc/eth_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ func TestEth_DecodeTxn(t *testing.T) {
{
name: "should be successful",
arg: &txnArgs{
From: &addr1,
To: &addr2,
Gas: toArgUint64Ptr(21000),
GasPrice: toArgBytesPtr(big.NewInt(10000).Bytes()),
GasTipCap: toArgBytesPtr(big.NewInt(10000).Bytes()),
GasFeeCap: toArgBytesPtr(big.NewInt(10000).Bytes()),
Value: toArgBytesPtr(oneEther.Bytes()),
Data: nil,
Nonce: toArgUint64Ptr(0),
From: &addr1,
To: &addr2,
Gas: toArgUint64Ptr(21000),
GasPrice: toArgBytesPtr(big.NewInt(10000).Bytes()),
MaxPriorityFeePerGas: toArgBytesPtr(big.NewInt(10000).Bytes()),
MaxFeePerGas: toArgBytesPtr(big.NewInt(10000).Bytes()),
Value: toArgBytesPtr(oneEther.Bytes()),
Data: nil,
Nonce: toArgUint64Ptr(0),
},
res: &types.Transaction{
From: addr1,
Expand Down Expand Up @@ -266,16 +266,16 @@ func TestEth_TxnType(t *testing.T) {

// Setup Txn
args := &txnArgs{
From: &addr1,
To: &addr2,
Gas: toArgUint64Ptr(21000),
GasPrice: toArgBytesPtr(big.NewInt(10000).Bytes()),
GasTipCap: toArgBytesPtr(big.NewInt(10000).Bytes()),
GasFeeCap: toArgBytesPtr(big.NewInt(10000).Bytes()),
Value: toArgBytesPtr(oneEther.Bytes()),
Data: nil,
Nonce: toArgUint64Ptr(0),
Type: toArgUint64Ptr(uint64(types.DynamicFeeTx)),
From: &addr1,
To: &addr2,
Gas: toArgUint64Ptr(21000),
GasPrice: toArgBytesPtr(big.NewInt(10000).Bytes()),
MaxPriorityFeePerGas: toArgBytesPtr(big.NewInt(10000).Bytes()),
MaxFeePerGas: toArgBytesPtr(big.NewInt(10000).Bytes()),
Value: toArgBytesPtr(oneEther.Bytes()),
Data: nil,
Nonce: toArgUint64Ptr(0),
Type: toArgUint64Ptr(uint64(types.DynamicFeeTx)),
}

expectedRes := &types.Transaction{
Expand Down
12 changes: 6 additions & 6 deletions jsonrpc/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,12 @@ func DecodeTxn(arg *txnArgs, store nonceGetter, forceSetNonce bool) (*types.Tran
arg.GasPrice = argBytesPtr([]byte{})
}

if arg.GasTipCap == nil {
arg.GasTipCap = argBytesPtr([]byte{})
if arg.MaxPriorityFeePerGas == nil {
arg.MaxPriorityFeePerGas = argBytesPtr([]byte{})
}

if arg.GasFeeCap == nil {
arg.GasFeeCap = argBytesPtr([]byte{})
if arg.MaxFeePerGas == nil {
arg.MaxFeePerGas = argBytesPtr([]byte{})
}

var input []byte
Expand Down Expand Up @@ -223,8 +223,8 @@ func DecodeTxn(arg *txnArgs, store nonceGetter, forceSetNonce bool) (*types.Tran
From: *arg.From,
Gas: uint64(*arg.Gas),
GasPrice: new(big.Int).SetBytes(*arg.GasPrice),
GasTipCap: new(big.Int).SetBytes(*arg.GasTipCap),
GasFeeCap: new(big.Int).SetBytes(*arg.GasFeeCap),
GasTipCap: new(big.Int).SetBytes(*arg.MaxPriorityFeePerGas),
GasFeeCap: new(big.Int).SetBytes(*arg.MaxFeePerGas),
Value: new(big.Int).SetBytes(*arg.Value),
Input: input,
Nonce: uint64(*arg.Nonce),
Expand Down
18 changes: 9 additions & 9 deletions jsonrpc/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,15 +696,15 @@ func TestDecodeTxn(t *testing.T) {
{
name: "should return mapped transaction",
arg: &txnArgs{
From: &from,
To: &to,
Gas: &gas,
GasPrice: &gasPrice,
GasTipCap: &gasTipCap,
GasFeeCap: &gasFeeCap,
Value: &value,
Input: &input,
Nonce: &nonce,
From: &from,
To: &to,
Gas: &gas,
GasPrice: &gasPrice,
MaxPriorityFeePerGas: &gasTipCap,
MaxFeePerGas: &gasFeeCap,
Value: &value,
Input: &input,
Nonce: &nonce,
},
store: &debugEndpointMockStore{},
expected: &types.Transaction{
Expand Down
172 changes: 161 additions & 11 deletions jsonrpc/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package jsonrpc

import (
"errors"
"fmt"
"math/big"
"strconv"
"strings"
Expand Down Expand Up @@ -376,17 +378,165 @@ func encodeToHex(b []byte) []byte {

// txnArgs is the transaction argument for the rpc endpoints
type txnArgs struct {
From *types.Address
To *types.Address
Gas *argUint64
GasPrice *argBytes
GasTipCap *argBytes
GasFeeCap *argBytes
Value *argBytes
Data *argBytes
Input *argBytes
Nonce *argUint64
Type *argUint64
From *types.Address `json:"from"`
To *types.Address `json:"to"`
Gas *argUint64 `json:"gas"`
GasPrice *argBytes `json:"gasPrice"`
MaxFeePerGas *argBytes `json:"maxFeePerGas"`
MaxPriorityFeePerGas *argBytes `json:"maxPriorityFeePerGas"`
Value *argBytes `json:"value"`
Data *argBytes `json:"data"`
Input *argBytes `json:"input"`
Nonce *argUint64 `json:"nonce"`
Type *argUint64 `json:"type"`
}

// data retrieves the transaction calldata. Input field is preferred.
func (args *txnArgs) data() []byte {
if args.Input != nil {
return *args.Input
}

if args.Data != nil {
return *args.Data
}

return nil
}

func (args *txnArgs) setDefaults(priceLimit uint64, eth *Eth) error {
if err := args.setFeeDefaults(priceLimit, eth.store); err != nil {
return err
}

if args.Nonce == nil {
args.Nonce = argUintPtr(eth.store.GetNonce(*args.From))
}

if args.Gas == nil {
// These fields are immutable during the estimation, safe to
// pass the pointer directly.
data := args.data()
callArgs := txnArgs{
From: args.From,
To: args.To,
GasPrice: args.GasPrice,
MaxFeePerGas: args.MaxFeePerGas,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
Value: args.Value,
Data: argBytesPtr(data),
}

estimatedGas, err := eth.EstimateGas(&callArgs, nil)
if err != nil {
return err
}

estimatedGasUint64, ok := estimatedGas.(argUint64)
if !ok {
return errors.New("estimated gas not a uint64")
}

args.Gas = &estimatedGasUint64
}

return nil
}

// setFeeDefaults fills in default fee values for unspecified tx fields.
func (args *txnArgs) setFeeDefaults(priceLimit uint64, store ethStore) error {
// If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}

// If the tx has completely specified a fee mechanism, no default is needed.
// This allows users who are not yet synced past London to get defaults for
// other tx values. See https://github.com/ethereum/go-ethereum/pull/23274
// for more information.
eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil

// Sanity check the EIP-1559 fee parameters if present.
if args.GasPrice == nil && eip1559ParamsSet {
maxFeePerGas := new(big.Int).SetBytes(*args.MaxFeePerGas)
maxPriorityFeePerGas := new(big.Int).SetBytes(*args.MaxPriorityFeePerGas)

if maxFeePerGas.Sign() == 0 {
return errors.New("maxFeePerGas must be non-zero")
}

if maxFeePerGas.Cmp(maxPriorityFeePerGas) < 0 {
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
}

args.Type = argUintPtr(uint64(types.DynamicFeeTx))

return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
}

// Sanity check the non-EIP-1559 fee parameters.
head := store.Header()
isLondon := store.GetForksInTime(head.Number).London

if args.GasPrice != nil && !eip1559ParamsSet {
// Zero gas-price is not allowed after London fork
if new(big.Int).SetBytes(*args.GasPrice).Sign() == 0 && isLondon {
return errors.New("gasPrice must be non-zero after london fork")
}

return nil // No need to set anything, user already set GasPrice
}

// Now attempt to fill in default value depending on whether London is active or not.
if isLondon {
// London is active, set maxPriorityFeePerGas and maxFeePerGas.
if err := args.setLondonFeeDefaults(head, store); err != nil {
return err
}
} else {
if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil {
return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active")
}

// London not active, set gas price.
avgGasPrice := store.GetAvgGasPrice()

args.GasPrice = argBytesPtr(common.BigMax(new(big.Int).SetUint64(priceLimit), avgGasPrice).Bytes())
}
return nil

Check failure on line 506 in jsonrpc/types.go

View workflow job for this annotation

GitHub Actions / Linter

return statements should not be cuddled if block has more than two lines (wsl)

Check failure on line 506 in jsonrpc/types.go

View workflow job for this annotation

GitHub Actions / Linter

return statements should not be cuddled if block has more than two lines (wsl)
}

// setLondonFeeDefaults fills in reasonable default fee values for unspecified fields.
func (args *txnArgs) setLondonFeeDefaults(head *types.Header, store ethStore) error {
// Set maxPriorityFeePerGas if it is missing.
if args.MaxPriorityFeePerGas == nil {
tip, err := store.MaxPriorityFeePerGas()
if err != nil {
return err
}
args.MaxPriorityFeePerGas = argBytesPtr(tip.Bytes())

Check failure on line 517 in jsonrpc/types.go

View workflow job for this annotation

GitHub Actions / Linter

assignments should only be cuddled with other assignments (wsl)

Check failure on line 517 in jsonrpc/types.go

View workflow job for this annotation

GitHub Actions / Linter

assignments should only be cuddled with other assignments (wsl)
}

// Set maxFeePerGas if it is missing.
if args.MaxFeePerGas == nil {
// Set the max fee to be 2 times larger than the previous block's base fee.
// The additional slack allows the tx to not become invalidated if the base
// fee is rising.
val := new(big.Int).Add(
new(big.Int).SetBytes(*args.MaxPriorityFeePerGas),
new(big.Int).Mul(new(big.Int).SetUint64(head.BaseFee), big.NewInt(2)),
)
args.MaxFeePerGas = argBytesPtr(val.Bytes())
}

// Both EIP-1559 fee parameters are now set; sanity check them.
if new(big.Int).SetBytes(*args.MaxFeePerGas).Cmp(new(big.Int).SetBytes(*args.MaxPriorityFeePerGas)) < 0 {
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
}

args.Type = argUintPtr(uint64(types.DynamicFeeTx))

return nil
}

type progression struct {
Expand Down

0 comments on commit 86faf3f

Please sign in to comment.