/
receipts_basic.go
87 lines (76 loc) · 2.31 KB
/
receipts_basic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package sources
import (
"context"
"io"
"sync"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)
type receiptsBatchCall = batching.IterativeBatchCall[common.Hash, *types.Receipt]
type BasicRPCReceiptsFetcher struct {
client rpcClient
maxBatchSize int
// calls caches uncompleted batch calls
calls map[common.Hash]*receiptsBatchCall
callsMu sync.Mutex
}
func NewBasicRPCReceiptsFetcher(client rpcClient, maxBatchSize int) *BasicRPCReceiptsFetcher {
return &BasicRPCReceiptsFetcher{
client: client,
maxBatchSize: maxBatchSize,
calls: make(map[common.Hash]*receiptsBatchCall),
}
}
// FetchReceipts fetches receipts for the given block and transaction hashes
// it does not validate receipts, and expects the caller to do so
func (f *BasicRPCReceiptsFetcher) FetchReceipts(ctx context.Context, blockInfo eth.BlockInfo, txHashes []common.Hash) (types.Receipts, error) {
block := eth.ToBlockID(blockInfo)
call := f.getOrCreateBatchCall(block.Hash, txHashes)
// Fetch all receipts
for {
if err := call.Fetch(ctx); err == io.EOF {
break
} else if err != nil {
return nil, err
}
}
res, err := call.Result()
if err != nil {
return nil, err
}
// call successful, remove from cache
f.deleteBatchCall(block.Hash)
return res, nil
}
func (f *BasicRPCReceiptsFetcher) getOrCreateBatchCall(blockHash common.Hash, txHashes []common.Hash) *receiptsBatchCall {
f.callsMu.Lock()
defer f.callsMu.Unlock()
if call, ok := f.calls[blockHash]; ok {
return call
}
call := batching.NewIterativeBatchCall[common.Hash, *types.Receipt](
txHashes,
makeReceiptRequest,
f.client.BatchCallContext,
f.client.CallContext,
f.maxBatchSize,
)
f.calls[blockHash] = call
return call
}
func (f *BasicRPCReceiptsFetcher) deleteBatchCall(blockHash common.Hash) {
f.callsMu.Lock()
defer f.callsMu.Unlock()
delete(f.calls, blockHash)
}
func makeReceiptRequest(txHash common.Hash) (*types.Receipt, rpc.BatchElem) {
out := new(types.Receipt)
return out, rpc.BatchElem{
Method: "eth_getTransactionReceipt",
Args: []any{txHash},
Result: &out, // receipt may become nil, double pointer is intentional
}
}