From 5f96284e877927a04a3f91dab115cff4db0789e5 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Fri, 15 Nov 2019 14:40:10 -0600 Subject: [PATCH] add fields to CallArgs and SendTxArgs; add guards to DoCall, setDefaults, and SendRawTransaction --- ethclient/ethclient.go | 6 +++ internal/ethapi/api.go | 85 +++++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index a60d73223..a62db56b7 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -534,5 +534,11 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } + if msg.GasPremium != nil { + arg["gasPremium"] = (*hexutil.Big)(msg.GasPremium) + } + if msg.FeeCap != nil { + arg["feeCap"] = (*hexutil.Big)(msg.FeeCap) + } return arg } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 522bff511..fcb6e5e3e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -732,12 +732,14 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A // CallArgs represents the arguments for a call. type CallArgs struct { - From *common.Address `json:"from"` - To *common.Address `json:"to"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Value *hexutil.Big `json:"value"` - Data *hexutil.Bytes `json:"data"` + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"data"` + GasPremium *hexutil.Big `json:"gasPremium"` + FeeCap *hexutil.Big `json:"feeCap"` } // ToMessage converts CallArgs to the Message type used by the core evm @@ -793,6 +795,23 @@ type account struct { func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) (*core.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) + // EIP1559 guards + if b.ChainConfig().IsEIP1559Finalized(b.CurrentBlock().Number()) && args.GasPremium == nil || args.FeeCap == nil || args.GasPrice != nil { + return nil, 0, false, fmt.Errorf("after block %d EIP1559 is finalized and transactions must contain a GasPremium and FeeCap and not contain a GasPrice", b.ChainConfig().EIP1559FinalizedBlock.Uint64()) + } + if !b.ChainConfig().IsEIP1559(b.CurrentBlock().Number()) && args.GasPremium != nil || args.FeeCap != nil || args.GasPrice == nil { + return nil, 0, false, fmt.Errorf("before block %d EIP1559 is not activated and transactions must contain a GasPrice and not contain a GasPremium or FeeCap", b.ChainConfig().EIP1559Block.Uint64()) + } + if args.GasPrice != nil && (args.GasPremium != nil || args.FeeCap != nil) { + return nil, 0, false, errors.New("if GasPrice is set, GasPremium and FeeCap must not be set") + } + if args.FeeCap != nil && args.GasPremium == nil { + return nil, 0, false, errors.New("if FeeCap is set, GasPremium must be set") + } + if args.GasPremium != nil && args.FeeCap == nil { + return nil, 0, false, errors.New("if GasPremium is set, FeeCap must be set") + } + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err @@ -1447,11 +1466,31 @@ type SendTxArgs struct { // newer name and should be preferred by clients. Data *hexutil.Bytes `json:"data"` Input *hexutil.Bytes `json:"input"` + // EIP1559 fields + GasPremium *hexutil.Big `json:"gasPremium"` + FeeCap *hexutil.Big `json:"feeCap"` } // setDefaults is a helper function that fills in default values for unspecified tx fields. func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { - if args.GasPrice == nil { + // EIP1559 guards + if b.ChainConfig().IsEIP1559Finalized(b.CurrentBlock().Number()) && args.GasPremium == nil || args.FeeCap == nil || args.GasPrice != nil { + return fmt.Errorf("after block %d EIP1559 is finalized and transactions must contain a GasPremium and FeeCap and not contain a GasPrice", b.ChainConfig().EIP1559FinalizedBlock.Uint64()) + } + if !b.ChainConfig().IsEIP1559(b.CurrentBlock().Number()) && args.GasPremium != nil || args.FeeCap != nil || args.GasPrice == nil { + return fmt.Errorf("before block %d EIP1559 is not activated and transactions must contain a GasPrice and not contain a GasPremium or FeeCap", b.ChainConfig().EIP1559Block.Uint64()) + } + if args.GasPrice != nil && (args.GasPremium != nil || args.FeeCap != nil) { + return errors.New("if GasPrice is set, GasPremium and FeeCap must not be set") + } + if args.FeeCap != nil && args.GasPremium == nil { + return errors.New("if FeeCap is set, GasPremium must be set") + } + if args.GasPremium != nil && args.FeeCap == nil { + return errors.New("if GasPremium is set, FeeCap must be set") + } + // If EIP1559 is activated but not finalized and neither a GasPrice, GasPremium, or FeeCap are provided default to suggesting a GasPrice + if args.GasPrice == nil && args.GasPremium == nil { price, err := b.SuggestPrice(ctx) if err != nil { return err @@ -1492,11 +1531,13 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { input = args.Data } callArgs := CallArgs{ - From: &args.From, // From shouldn't be nil - To: args.To, - GasPrice: args.GasPrice, - Value: args.Value, - Data: input, + From: &args.From, // From shouldn't be nil + To: args.To, + GasPrice: args.GasPrice, + Value: args.Value, + Data: input, + GasPremium: args.GasPremium, + FeeCap: args.FeeCap, } pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) @@ -1517,9 +1558,9 @@ func (args *SendTxArgs) toTransaction() *types.Transaction { input = *args.Data } if args.To == nil { - return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, nil, nil) + return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, (*big.Int)(args.GasPremium), (*big.Int)(args.FeeCap)) } - return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, nil, nil) + return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input, (*big.Int)(args.GasPremium), (*big.Int)(args.FeeCap)) } // SubmitTransaction is a helper function that submits tx to txPool and logs a message. @@ -1596,6 +1637,22 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err } + // EIP1559 guards + if s.b.ChainConfig().IsEIP1559Finalized(s.b.CurrentBlock().Number()) && tx.GasPremium() == nil || tx.FeeCap() == nil || tx.GasPrice() != nil { + return common.Hash{}, fmt.Errorf("after block %d EIP1559 is finalized and transactions must contain a GasPremium and FeeCap and not contain a GasPrice", s.b.ChainConfig().EIP1559FinalizedBlock.Uint64()) + } + if !s.b.ChainConfig().IsEIP1559(s.b.CurrentBlock().Number()) && tx.GasPremium() != nil || tx.FeeCap() != nil || tx.GasPrice() == nil { + return common.Hash{}, fmt.Errorf("before block %d EIP1559 is not activated and transactions must contain a GasPrice and not contain a GasPremium or FeeCap", s.b.ChainConfig().EIP1559Block.Uint64()) + } + if tx.GasPrice() != nil && (tx.GasPremium() != nil || tx.FeeCap() != nil) { + return common.Hash{}, errors.New("if GasPrice is set, GasPremium and FeeCap must not be set") + } + if tx.FeeCap() != nil && tx.GasPremium() == nil { + return common.Hash{}, errors.New("if FeeCap is set, GasPremium must be set") + } + if tx.GasPremium() != nil && tx.FeeCap() == nil { + return common.Hash{}, errors.New("if GasPremium is set, FeeCap must be set") + } return SubmitTransaction(ctx, s.b, tx) }