Skip to content

Commit

Permalink
Redirect historical RPC requests (ethereum#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
maurelian authored and protolambda committed Nov 4, 2022
1 parent 0caa739 commit 1d1d135
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 11 deletions.
7 changes: 4 additions & 3 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
Expand Down Expand Up @@ -173,7 +174,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
return nil, nil, err
}
if header == nil {
return nil, nil, errors.New("header not found")
return nil, nil, ethapi.ErrHeaderNotFound
}
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
return stateDb, header, err
Expand Down Expand Up @@ -383,8 +384,8 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo
return b.eth.stateAtTransaction(block, txIndex, reexec)
}

func (b *EthAPIBackend) SequencerRPCService() *rpc.Client {
return b.eth.seqRPCService
func (b *EthAPIBackend) HistoricalRPCService() *rpc.Client {
return b.eth.historicalRPCService
}

func (b *EthAPIBackend) Genesis() *types.Block {
Expand Down
101 changes: 101 additions & 0 deletions ethclient/ethclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ import (
"errors"
"fmt"
"math/big"
"net"
"net/http"
"reflect"
"testing"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/ethapi"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
Expand Down Expand Up @@ -210,7 +215,62 @@ var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &
To: &common.Address{2},
})

type mockHistoricalBackend struct{}

func (m *mockHistoricalBackend) Call(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi.StateOverride) (hexutil.Bytes, error) {
num, ok := blockNrOrHash.Number()
if ok && num == 100 {
return hexutil.Bytes("test"), nil
}
return nil, ethapi.ErrHeaderNotFound
}

func (m *mockHistoricalBackend) EstimateGas(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Uint64, error) {
num, ok := blockNrOrHash.Number()
if ok && num == 100 {
return hexutil.Uint64(12345), nil
}
return 0, ethapi.ErrHeaderNotFound
}

func newMockHistoricalBackend(t *testing.T) string {
s := rpc.NewServer()
err := node.RegisterApis([]rpc.API{
{
Namespace: "eth",
Service: new(mockHistoricalBackend),
Public: true,
Authenticated: false,
},
}, nil, s)
if err != nil {
t.Fatalf("error creating mock historical backend: %v", err)
}

hdlr := node.NewHTTPHandlerStack(s, []string{"*"}, []string{"*"}, nil)
mux := http.NewServeMux()
mux.Handle("/", hdlr)

listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("error creating mock historical backend listener: %v", err)
}

go func() {
httpS := &http.Server{Handler: mux}
httpS.Serve(listener)

t.Cleanup(func() {
httpS.Shutdown(context.Background())
})
}()

return fmt.Sprintf("http://%s", listener.Addr().String())
}

func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
histAddr := newMockHistoricalBackend(t)

// Generate test chain.
blocks := generateTestChain()

Expand All @@ -222,6 +282,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
// Create Ethereum Service
config := &ethconfig.Config{Genesis: genesis}
config.Ethash.PowMode = ethash.ModeFake
config.RollupHistoricalRPC = histAddr
ethservice, err := eth.New(n, config)
if err != nil {
t.Fatalf("can't create new ethereum service: %v", err)
Expand Down Expand Up @@ -289,6 +350,9 @@ func TestEthClient(t *testing.T) {
"TransactionSender": {
func(t *testing.T) { testTransactionSender(t, client) },
},
"EstimateGas": {
func(t *testing.T) { testEstimateGas(t, client) },
},
}

t.Parallel()
Expand Down Expand Up @@ -580,6 +644,14 @@ func testCallContract(t *testing.T, client *rpc.Client) {
if _, err := ec.PendingCallContract(context.Background(), msg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Historical
histVal, err := ec.CallContract(context.Background(), msg, big.NewInt(100))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(histVal) != "test" {
t.Fatalf("expected %s to equal test", string(histVal))
}
}

func testAtFunctions(t *testing.T, client *rpc.Client) {
Expand Down Expand Up @@ -687,6 +759,35 @@ func testTransactionSender(t *testing.T, client *rpc.Client) {
}
}

func testEstimateGas(t *testing.T, client *rpc.Client) {
ec := NewClient(client)

// EstimateGas
msg := ethereum.CallMsg{
From: testAddr,
To: &common.Address{},
Gas: 21000,
Value: big.NewInt(1),
}
gas, err := ec.EstimateGas(context.Background(), msg)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gas != 21000 {
t.Fatalf("unexpected gas price: %v", gas)
}

// historical case
var res hexutil.Uint64
err = client.CallContext(context.Background(), &res, "eth_estimateGas", toCallArg(msg), rpc.BlockNumberOrHashWithNumber(100))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res != 12345 {
t.Fatalf("invalid result: %d", res)
}
}

func sendTransaction(ec *Client) error {
chainID, err := ec.ChainID(context.Background())
if err != nil {
Expand Down
20 changes: 18 additions & 2 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)

var ErrHeaderNotFound = errors.New("header not found")

// EthereumAPI provides an API to access Ethereum related information.
type EthereumAPI struct {
b Backend
Expand Down Expand Up @@ -1043,7 +1045,11 @@ func (e *revertError) ErrorData() interface{} {
// useful to execute and retrieve values.
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil {
if errors.Is(err, ErrHeaderNotFound) && s.b.HistoricalRPCService() != nil {
var histResult hexutil.Bytes
err = s.b.HistoricalRPCService().CallContext(ctx, &histResult, "eth_call", args, blockNrOrHash, overrides)
return histResult, err
} else if err != nil {
return nil, err
}
// If the result contains a revert reason, try to unpack and return it.
Expand Down Expand Up @@ -1180,7 +1186,17 @@ func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, b
if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash
}
return DoEstimateGas(ctx, s.b, args, bNrOrHash, s.b.RPCGasCap())

res, err := DoEstimateGas(ctx, s.b, args, bNrOrHash, s.b.RPCGasCap())
if errors.Is(err, ErrHeaderNotFound) && s.b.HistoricalRPCService() != nil {
var result hexutil.Uint64
err := s.b.HistoricalRPCService().CallContext(ctx, &result, "eth_estimateGas", args, blockNrOrHash)
return result, err
} else if err != nil {
return 0, err
}

return res, err
}

// RPCMarshalHeader converts the given header to the RPC output .
Expand Down
2 changes: 1 addition & 1 deletion internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type Backend interface {

ChainConfig() *params.ChainConfig
Engine() consensus.Engine
SequencerRPCService() *rpc.Client
HistoricalRPCService() *rpc.Client
Genesis() *types.Block

// eth/filters needs to be initialized from this backend type, so methods needed by
Expand Down
6 changes: 3 additions & 3 deletions internal/ethapi/transaction_args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,6 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
return nil
}

func (b *backendMock) Engine() consensus.Engine { return nil }
func (b *backendMock) SequencerRPCService() *rpc.Client { return nil }
func (b *backendMock) Genesis() *types.Block { return nil }
func (b *backendMock) Engine() consensus.Engine { return nil }
func (b *backendMock) HistoricalRPCService() *rpc.Client { return nil }
func (b *backendMock) Genesis() *types.Block { return nil }
4 changes: 2 additions & 2 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Blo
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}

func (b *LesApiBackend) SequencerRPCService() *rpc.Client {
return b.eth.seqRPCService
func (b *LesApiBackend) HistoricalRPCService() *rpc.Client {
return b.eth.historicalRPCService
}

func (b *LesApiBackend) Genesis() *types.Block {
Expand Down

0 comments on commit 1d1d135

Please sign in to comment.