Skip to content

Commit

Permalink
internal/ethapi: implement API for EIP-4337
Browse files Browse the repository at this point in the history
  • Loading branch information
MatusKysel committed Mar 14, 2023
1 parent 0d87a13 commit 973a830
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 0 deletions.
4 changes: 4 additions & 0 deletions accounts/abi/bind/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
)

var (
Expand Down Expand Up @@ -96,6 +97,9 @@ type ContractTransactor interface {

// SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(ctx context.Context, tx *types.Transaction) error

// SendTransactionConditional injects the conditional transaction into the pending pool for execution after verification.
SendTransactionConditional(ctx context.Context, tx *types.Transaction, opts ethapi.TransactionOpts) error
}

// ContractFilterer defines the methods needed to access log events using one-off
Expand Down
6 changes: 6 additions & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
Expand Down Expand Up @@ -677,6 +678,11 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
return nil
}

// SendTransaction updates the pending block to include the given transaction.
func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *types.Transaction, opts ethapi.TransactionOpts) error {
return nil
}

// FilterLogs executes a log filter operation, blocking during execution and
// returning all the results in one batch.
//
Expand Down
5 changes: 5 additions & 0 deletions accounts/abi/bind/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -74,6 +75,10 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
return nil
}

func (mt *mockTransactor) SendTransactionConditional(ctx context.Context, tx *types.Transaction, opts ethapi.TransactionOpts) error {
return nil
}

type mockCaller struct {
codeAtBlockNumber *big.Int
callContractBlockNumber *big.Int
Expand Down
13 changes: 13 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
)

Expand Down Expand Up @@ -571,6 +572,18 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data))
}

// SendTransactionConditional injects a signed transaction into the pending pool for execution.
//
// If the transaction was a contract creation use the TransactionReceipt method to get the
// contract address after the transaction has been mined.
func (ec *Client) SendTransactionConditional(ctx context.Context, tx *types.Transaction, opts ethapi.TransactionOpts) error {
data, err := tx.MarshalBinary()
if err != nil {
return err
}
return ec.c.CallContext(ctx, nil, "eth_sendRawTransactionConditional", hexutil.Encode(data), opts)
}

func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
Expand Down
46 changes: 46 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,42 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
return tx.Hash(), nil
}

func SubmitConditionalTransaction(ctx context.Context, b Backend, tx *types.Transaction, options TransactionOpts) (common.Hash, error) {
// If the transaction fee cap is already specified, ensure the
// fee of the given transaction is _reasonable_.
if err := checkTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil {
return common.Hash{}, err
}
if !b.UnprotectedAllowed() && !tx.Protected() {
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
}
state, _, err := b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(b.CurrentBlock().Number().Int64()))
if state == nil || err != nil {
return common.Hash{}, err
}
if err := options.Check(b.CurrentBlock().NumberU64(), b.CurrentBlock().Time(), state); err != nil {
return common.Hash{}, err
}
if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
// Print a log with full tx details for manual investigations and interventions
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
from, err := types.Sender(signer, tx)
if err != nil {
return common.Hash{}, err
}

if tx.To() == nil {
addr := crypto.CreateAddress(from, tx.Nonce())
log.Info("Submitted contract creation", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "contract", addr.Hex(), "value", tx.Value())
} else {
log.Info("Submitted transaction", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "recipient", tx.To(), "value", tx.Value())
}
return tx.Hash(), nil
}

// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) {
Expand Down Expand Up @@ -2137,6 +2173,16 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input
return SubmitTransaction(ctx, s.b, tx)
}

// SendRawTransactionConditional will add the signed transaction to the transaction pool.
// The sender/bundler is responsible for signing the transaction
func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, opts TransactionOpts) (common.Hash, error) {
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(input); err != nil {
return common.Hash{}, err
}
return SubmitConditionalTransaction(ctx, s.b, tx, opts)
}

// Sign calculates an ECDSA signature for:
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message).
//
Expand Down
81 changes: 81 additions & 0 deletions internal/ethapi/transaction_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package ethapi

import (
"bytes"
"encoding/json"
"errors"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
)

type AccountStorage struct {
RootHash *common.Hash
SlotValue map[common.Hash]common.Hash
}

func (r *AccountStorage) UnmarshalJSON(data []byte) error {
var hash common.Hash
if err := json.Unmarshal(data, &hash); err == nil {
r.RootHash = &hash
return nil
}
return json.Unmarshal(data, &r.SlotValue)
}

func (r AccountStorage) MarshalJSON() ([]byte, error) {
if r.RootHash != nil {
return json.Marshal(*r.RootHash)
}
return json.Marshal(r.SlotValue)
}

type TransactionOpts struct {
KnownAccounts map[common.Address]AccountStorage `json:"knownAccounts"`
BlockNumberMin *hexutil.Uint64 `json:"blockNumberMin,omitempty"`
BlockNumberMax *hexutil.Uint64 `json:"blockNumberMax,omitempty"`
TimestampMin *hexutil.Uint64 `json:"timestampMin,omitempty"`
TimestampMax *hexutil.Uint64 `json:"timestampMax,omitempty"`
}

func (o *TransactionOpts) Check(blockNumber uint64, timeStamp uint64, statedb *state.StateDB) error {
if o.BlockNumberMin != nil && blockNumber < uint64(*o.BlockNumberMin) {
return errors.New("BlockNumberMin condition not met")
}
if o.BlockNumberMax != nil && blockNumber > uint64(*o.BlockNumberMax) {
return errors.New("BlockNumberMax condition not met")
}
if o.TimestampMin != nil && timeStamp < uint64(*o.TimestampMin) {
return errors.New("TimestampMin condition not met")
}
if o.TimestampMax != nil && timeStamp > uint64(*o.TimestampMax) {
return errors.New("TimestampMax condition not met")
}
if len(o.KnownAccounts) > 1000 {
return errors.New("knownAccounts too large")
}
return o.CheckOnlyStorage(statedb)
}

func (o *TransactionOpts) CheckOnlyStorage(statedb *state.StateDB) error {
for address, AccountStorage := range o.KnownAccounts {
if AccountStorage.RootHash != nil {
trie := statedb.StorageTrie(address)
if trie == nil {
return errors.New("Storage trie not found for address key in knownAccounts option")
}
if trie.Hash() != *AccountStorage.RootHash {
return errors.New("Storage root hash condition not met")
}
} else if len(AccountStorage.SlotValue) > 0 {
for slot, value := range AccountStorage.SlotValue {
stored := statedb.GetState(address, slot)
if !bytes.Equal(stored.Bytes(), value.Bytes()) {
return errors.New("Storage slot value condition not met")
}
}
} // else AccountStorage.SlotValue is empty - ignore it and check the rest of conditions
}
return nil
}
8 changes: 8 additions & 0 deletions internal/jsre/deps/web3.js
Original file line number Diff line number Diff line change
Expand Up @@ -5438,6 +5438,13 @@ var methods = function () {
inputFormatter: [null]
});

var sendRawTransaction = new Method({
name: 'sendRawTransactionConditional',
call: 'eth_sendRawTransactionConditional',
params: 2,
inputFormatter: [null]
});

var sendTransaction = new Method({
name: 'sendTransaction',
call: 'eth_sendTransaction',
Expand Down Expand Up @@ -5523,6 +5530,7 @@ var methods = function () {
call,
estimateGas,
sendRawTransaction,
sendRawTransactionConditional,
signTransaction,
sendTransaction,
sign,
Expand Down

0 comments on commit 973a830

Please sign in to comment.