Skip to content

Commit 241a8a7

Browse files
authored
Add support for eth_getBlockReceipts RPC method (#102)
### TL;DR Added support for using `eth_getBlockReceipts` instead of `eth_getLogs` and enhanced transaction data storage. ### What changed? - Introduced `eth_getBlockReceipts` RPC method support - Added new configuration options for block receipts - Enhanced transaction structure with additional fields from receipts - Updated ClickHouse schema to accommodate new transaction fields - Improved serialization logic to handle block receipts data ### How to test? 1. Update the configuration file to enable block receipts: ```yaml rpc: blockReceipts: enabled: true blocksPerRequest: 500 batchDelay: 100 ``` 2. Run the indexer with the updated configuration 3. Verify that transactions in the ClickHouse database contain the new fields (e.g., `contract_address`, `gas_used`, `status`) 4. Check the logs to ensure that `eth_getBlockReceipts` method is being used when enabled ### Why make this change? This change improves the efficiency and completeness of data collection: 1. Using `eth_getBlockReceipts` can be more efficient than fetching logs and receipts separately 2. Additional transaction data from receipts provides more comprehensive information for analysis and querying 3. The enhanced schema allows for more detailed transaction insights without the need for additional RPC calls
2 parents 0beab4a + e3c8949 commit 241a8a7

File tree

12 files changed

+337
-69
lines changed

12 files changed

+337
-69
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,43 @@ rpc:
136136
batchDelay: 100
137137
```
138138

139+
#### RPC Block Receipts Enabled
140+
If this is `true`, will use `eth_getBlockReceipts` instead of `eth_getLogs` if the RPC supports it. Allows getting receipt data for transactions, but is not supported by every RPC. Default is `false`.
141+
142+
cmd: `--rpc-block-receipts-enabled`
143+
env: `RPC_BLOCKRECEIPTS_ENABLED`
144+
yaml:
145+
```yaml
146+
rpc:
147+
blockReceipts:
148+
enabled: true
149+
```
150+
151+
#### RPC Block Receipts Blocks Per Request
152+
How many blocks at a time to fetch block receipts for from the RPC. Default is 250.
153+
Has no effect if it's larger than RPC blocks per request.
154+
155+
cmd: `--rpc-block-receipts-blocksPerRequest`
156+
env: `RPC_BLOCKRECEIPTS_BLOCKSPERREQUEST`
157+
yaml:
158+
```yaml
159+
rpc:
160+
blockReceipts:
161+
blocksPerRequest: 100
162+
```
163+
164+
#### RPC Block Receipts Batch Delay
165+
Milliseconds to wait between batches of block receipts when fetching from the RPC. Default is 0.
166+
167+
cmd: `--rpc-block-receipts-batchDelay`
168+
env: `RPC_BLOCKRECEIPTS_BATCHDELAY`
169+
yaml:
170+
```yaml
171+
rpc:
172+
blockReceipts:
173+
batchDelay: 100
174+
```
175+
139176
#### RPC Traces Enabled
140177
Whether to enable fetching traces from the RPC. Default is `true`, but it will try to detect if the RPC supports traces automatically.
141178

cmd/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func init() {
4141
rootCmd.PersistentFlags().Int("rpc-blocks-batchDelay", 0, "Milliseconds to wait between batches of blocks when fetching from the RPC")
4242
rootCmd.PersistentFlags().Int("rpc-logs-blocksPerRequest", 0, "How many blocks to fetch logs per request")
4343
rootCmd.PersistentFlags().Int("rpc-logs-batchDelay", 0, "Milliseconds to wait between batches of logs when fetching from the RPC")
44+
rootCmd.PersistentFlags().Bool("rpc-blockReceipts-enabled", false, "Whether to enable fetching block receipts from the RPC")
45+
rootCmd.PersistentFlags().Int("rpc-blockReceipts-blocksPerRequest", 0, "How many blocks to fetch receipts for per request")
46+
rootCmd.PersistentFlags().Int("rpc-blockReceipts-batchDelay", 0, "Milliseconds to wait between batches of receipts when fetching from the RPC")
4447
rootCmd.PersistentFlags().Bool("rpc-traces-enabled", true, "Whether to enable fetching traces from the RPC")
4548
rootCmd.PersistentFlags().Int("rpc-traces-blocksPerRequest", 0, "How many blocks to fetch traces per request")
4649
rootCmd.PersistentFlags().Int("rpc-traces-batchDelay", 0, "Milliseconds to wait between batches of traces when fetching from the RPC")
@@ -89,6 +92,9 @@ func init() {
8992
viper.BindPFlag("rpc.blocks.batchDelay", rootCmd.PersistentFlags().Lookup("rpc-blocks-batchDelay"))
9093
viper.BindPFlag("rpc.logs.blocksPerRequest", rootCmd.PersistentFlags().Lookup("rpc-logs-blocksPerRequest"))
9194
viper.BindPFlag("rpc.logs.batchDelay", rootCmd.PersistentFlags().Lookup("rpc-logs-batchDelay"))
95+
viper.BindPFlag("rpc.blockReceipts.enabled", rootCmd.PersistentFlags().Lookup("rpc-blockReceipts-enabled"))
96+
viper.BindPFlag("rpc.blockReceipts.blocksPerRequest", rootCmd.PersistentFlags().Lookup("rpc-blockReceipts-blocksPerRequest"))
97+
viper.BindPFlag("rpc.blockReceipts.batchDelay", rootCmd.PersistentFlags().Lookup("rpc-blockReceipts-batchDelay"))
9298
viper.BindPFlag("rpc.traces.enabled", rootCmd.PersistentFlags().Lookup("rpc-traces-enabled"))
9399
viper.BindPFlag("rpc.traces.blocksPerRequest", rootCmd.PersistentFlags().Lookup("rpc-traces-blocksPerRequest"))
94100
viper.BindPFlag("rpc.traces.batchDelay", rootCmd.PersistentFlags().Lookup("rpc-traces-batchDelay"))

configs/config.example.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ rpc:
55
logs:
66
blocksPerRequest: 400
77
batchDelay: 100
8+
blockReceipts:
9+
enabled: true
10+
blocksPerRequest: 500
11+
batchDelay: 100
812
traces:
913
enabled: true
1014
blocksPerRequest: 200
@@ -29,6 +33,11 @@ failureRecoverer:
2933
interval: 10000
3034
blocksPerRun: 100
3135

36+
reorgHandler:
37+
enabled: true
38+
interval: 1000
39+
blocksPerScan: 50
40+
3241
storage:
3342
main:
3443
clickhouse:

configs/config.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,22 @@ type RedisConfig struct {
8181
DB int `mapstructure:"db"`
8282
}
8383

84-
type RPCBatchSizeConfig struct {
84+
type RPCBatchRequestConfig struct {
8585
BlocksPerRequest int `mapstructure:"blocksPerRequest"`
8686
BatchDelay int `mapstructure:"batchDelay"`
8787
}
8888

89-
type RPCTracesConfig struct {
90-
Enabled bool `mapstructure:"enabled"`
91-
BlocksPerRequest int `mapstructure:"blocksPerRequest"`
92-
BatchDelay int `mapstructure:"batchDelay"`
89+
type ToggleableRPCBatchRequestConfig struct {
90+
Enabled bool `mapstructure:"enabled"`
91+
RPCBatchRequestConfig
9392
}
9493

9594
type RPCConfig struct {
96-
URL string `mapstructure:"url"`
97-
Blocks RPCBatchSizeConfig `mapstructure:"blocks"`
98-
Logs RPCBatchSizeConfig `mapstructure:"logs"`
99-
Traces RPCTracesConfig `mapstructure:"traces"`
95+
URL string `mapstructure:"url"`
96+
Blocks RPCBatchRequestConfig `mapstructure:"blocks"`
97+
Logs RPCBatchRequestConfig `mapstructure:"logs"`
98+
BlockReceipts ToggleableRPCBatchRequestConfig `mapstructure:"blockReceipts"`
99+
Traces ToggleableRPCBatchRequestConfig `mapstructure:"traces"`
100100
}
101101

102102
type APIConfig struct {

internal/common/log.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ type Log struct {
1818
}
1919

2020
type RawLogs = []map[string]interface{}
21+
type RawReceipts = []RawReceipt
22+
type RawReceipt = map[string]interface{}

internal/common/transaction.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,13 @@ type Transaction struct {
2525
R *big.Int `json:"r"`
2626
S *big.Int `json:"s"`
2727
V *big.Int `json:"v"`
28-
AccessListJson string `json:"access_list_json"`
28+
AccessListJson *string `json:"access_list_json"`
29+
ContractAddress *string `json:"contract_address"`
30+
GasUsed *uint64 `json:"gas_used"`
31+
CumulativeGasUsed *uint64 `json:"cumulative_gas_used"`
32+
EffectiveGasPrice *big.Int `json:"effective_gas_price"`
33+
BlobGasUsed *uint64 `json:"blob_gas_used"`
34+
BlobGasPrice *big.Int `json:"blob_gas_price"`
35+
LogsBloom *string `json:"logs_bloom"`
36+
Status *uint64 `json:"status"`
2937
}

internal/rpc/params.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ func GetLogsParams(blockNum *big.Int) []interface{} {
2121
func TraceBlockParams(blockNum *big.Int) []interface{} {
2222
return []interface{}{hexutil.EncodeBig(blockNum)}
2323
}
24+
25+
func GetBlockReceiptsParams(blockNum *big.Int) []interface{} {
26+
return []interface{}{hexutil.EncodeBig(blockNum)}
27+
}

internal/rpc/rpc.go

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ type GetBlocksResult struct {
2727
}
2828

2929
type BlocksPerRequestConfig struct {
30-
Blocks int
31-
Logs int
32-
Traces int
30+
Blocks int
31+
Logs int
32+
Traces int
33+
Receipts int
3334
}
3435

3536
type IRPCClient interface {
@@ -44,13 +45,14 @@ type IRPCClient interface {
4445
}
4546

4647
type Client struct {
47-
RPCClient *gethRpc.Client
48-
EthClient *ethclient.Client
49-
supportsTraceBlock bool
50-
isWebsocket bool
51-
url string
52-
chainID *big.Int
53-
blocksPerRequest BlocksPerRequestConfig
48+
RPCClient *gethRpc.Client
49+
EthClient *ethclient.Client
50+
supportsTraceBlock bool
51+
supportsBlockReceipts bool
52+
isWebsocket bool
53+
url string
54+
chainID *big.Int
55+
blocksPerRequest BlocksPerRequestConfig
5456
}
5557

5658
func Initialize() (IRPCClient, error) {
@@ -111,28 +113,75 @@ func (rpc *Client) Close() {
111113
}
112114

113115
func (rpc *Client) checkSupportedMethods() error {
116+
if err := rpc.checkGetBlockByNumberSupport(); err != nil {
117+
return err
118+
}
119+
if err := rpc.checkGetBlockReceiptsSupport(); err != nil {
120+
return err
121+
}
122+
if err := rpc.checkGetLogsSupport(); err != nil {
123+
return err
124+
}
125+
if err := rpc.checkTraceBlockSupport(); err != nil {
126+
return err
127+
}
128+
return nil
129+
}
130+
131+
func (rpc *Client) checkGetBlockByNumberSupport() error {
114132
var blockByNumberResult interface{}
115133
err := rpc.RPCClient.Call(&blockByNumberResult, "eth_getBlockByNumber", "latest", true)
116134
if err != nil {
117135
return fmt.Errorf("eth_getBlockByNumber method not supported: %v", err)
118136
}
119137
log.Debug().Msg("eth_getBlockByNumber method supported")
138+
return nil
139+
}
120140

141+
func (rpc *Client) checkGetBlockReceiptsSupport() error {
142+
if config.Cfg.RPC.BlockReceipts.Enabled {
143+
var getBlockReceiptsResult interface{}
144+
receiptsErr := rpc.RPCClient.Call(&getBlockReceiptsResult, "eth_getBlockReceipts", "latest")
145+
if receiptsErr != nil {
146+
log.Warn().Err(receiptsErr).Msg("eth_getBlockReceipts method not supported")
147+
return fmt.Errorf("eth_getBlockReceipts method not supported: %v", receiptsErr)
148+
} else {
149+
rpc.supportsBlockReceipts = true
150+
log.Debug().Msg("eth_getBlockReceipts method supported")
151+
}
152+
} else {
153+
rpc.supportsBlockReceipts = false
154+
log.Debug().Msg("eth_getBlockReceipts method disabled")
155+
}
156+
return nil
157+
}
158+
159+
func (rpc *Client) checkGetLogsSupport() error {
160+
if rpc.supportsBlockReceipts {
161+
return nil
162+
}
121163
var getLogsResult interface{}
122164
logsErr := rpc.RPCClient.Call(&getLogsResult, "eth_getLogs", map[string]string{"fromBlock": "0x0", "toBlock": "0x0"})
123165
if logsErr != nil {
124166
return fmt.Errorf("eth_getLogs method not supported: %v", logsErr)
125167
}
126168
log.Debug().Msg("eth_getLogs method supported")
169+
return nil
170+
}
127171

128-
var traceBlockResult interface{}
172+
func (rpc *Client) checkTraceBlockSupport() error {
129173
if config.Cfg.RPC.Traces.Enabled {
174+
var traceBlockResult interface{}
130175
if traceBlockErr := rpc.RPCClient.Call(&traceBlockResult, "trace_block", "latest"); traceBlockErr != nil {
131176
log.Warn().Err(traceBlockErr).Msg("Optional method trace_block not supported")
177+
} else {
178+
rpc.supportsTraceBlock = true
179+
log.Debug().Msg("trace_block method supported")
132180
}
181+
} else {
182+
rpc.supportsTraceBlock = false
183+
log.Debug().Msg("trace_block method disabled")
133184
}
134-
rpc.supportsTraceBlock = traceBlockResult != nil
135-
log.Debug().Msgf("trace_block method supported: %v", rpc.supportsTraceBlock)
136185
return nil
137186
}
138187

@@ -147,33 +196,44 @@ func (rpc *Client) setChainID() error {
147196

148197
func (rpc *Client) GetFullBlocks(blockNumbers []*big.Int) []GetFullBlockResult {
149198
var wg sync.WaitGroup
150-
var blocks []RPCFetchBatchResult[common.RawBlock]
151-
var logs []RPCFetchBatchResult[common.RawLogs]
152-
var traces []RPCFetchBatchResult[common.RawTraces]
153-
199+
var blocks *[]RPCFetchBatchResult[common.RawBlock]
200+
var logs *[]RPCFetchBatchResult[common.RawLogs]
201+
var traces *[]RPCFetchBatchResult[common.RawTraces]
202+
var receipts *[]RPCFetchBatchResult[common.RawReceipts]
154203
wg.Add(2)
155204

156205
go func() {
157206
defer wg.Done()
158-
blocks = RPCFetchBatch[common.RawBlock](rpc, blockNumbers, "eth_getBlockByNumber", GetBlockWithTransactionsParams)
207+
result := RPCFetchBatch[common.RawBlock](rpc, blockNumbers, "eth_getBlockByNumber", GetBlockWithTransactionsParams)
208+
blocks = &result
159209
}()
160210

161-
go func() {
162-
defer wg.Done()
163-
logs = RPCFetchInBatches[common.RawLogs](rpc, blockNumbers, rpc.blocksPerRequest.Logs, config.Cfg.RPC.Logs.BatchDelay, "eth_getLogs", GetLogsParams)
164-
}()
211+
if rpc.supportsBlockReceipts {
212+
go func() {
213+
defer wg.Done()
214+
result := RPCFetchInBatches[common.RawReceipts](rpc, blockNumbers, rpc.blocksPerRequest.Receipts, config.Cfg.RPC.BlockReceipts.BatchDelay, "eth_getBlockReceipts", GetBlockReceiptsParams)
215+
receipts = &result
216+
}()
217+
} else {
218+
go func() {
219+
defer wg.Done()
220+
result := RPCFetchInBatches[common.RawLogs](rpc, blockNumbers, rpc.blocksPerRequest.Logs, config.Cfg.RPC.Logs.BatchDelay, "eth_getLogs", GetLogsParams)
221+
logs = &result
222+
}()
223+
}
165224

166225
if rpc.supportsTraceBlock {
167226
wg.Add(1)
168227
go func() {
169228
defer wg.Done()
170-
traces = RPCFetchInBatches[common.RawTraces](rpc, blockNumbers, rpc.blocksPerRequest.Traces, config.Cfg.RPC.Traces.BatchDelay, "trace_block", TraceBlockParams)
229+
result := RPCFetchInBatches[common.RawTraces](rpc, blockNumbers, rpc.blocksPerRequest.Traces, config.Cfg.RPC.Traces.BatchDelay, "trace_block", TraceBlockParams)
230+
traces = &result
171231
}()
172232
}
173233

174234
wg.Wait()
175235

176-
return SerializeFullBlocks(rpc.chainID, blocks, logs, traces)
236+
return SerializeFullBlocks(rpc.chainID, blocks, logs, traces, receipts)
177237
}
178238

179239
func (rpc *Client) GetBlocks(blockNumbers []*big.Int) []GetBlocksResult {

0 commit comments

Comments
 (0)