reth_db.go 3.76 KB
Newer Older
1 2
//go:build rethdb

clabby's avatar
clabby committed
3 4 5
package sources

import (
6
	"context"
clabby's avatar
clabby committed
7
	"encoding/json"
clabby's avatar
clabby committed
8
	"fmt"
clabby's avatar
clabby committed
9 10
	"unsafe"

11 12 13
	"github.com/ethereum-optimism/optimism/op-service/client"
	"github.com/ethereum-optimism/optimism/op-service/eth"
	"github.com/ethereum-optimism/optimism/op-service/sources/caching"
clabby's avatar
clabby committed
14 15
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
16
	"github.com/ethereum/go-ethereum/log"
clabby's avatar
clabby committed
17 18 19 20
)

/*
#cgo LDFLAGS: -L../rethdb-reader/target/release -lrethdbreader
21
#include <stdarg.h>
clabby's avatar
clabby committed
22
#include <stdbool.h>
23 24
#include <stdint.h>
#include <stdlib.h>
clabby's avatar
clabby committed
25 26

typedef struct {
clabby's avatar
clabby committed
27 28
    char* data;
    size_t data_len;
clabby's avatar
clabby committed
29 30 31
    bool error;
} ReceiptsResult;

32 33 34 35 36 37
typedef struct OpenDBResult {
  const void *data;
  bool error;
} OpenDBResult;

extern ReceiptsResult rdb_read_receipts(const uint8_t* block_hash, size_t block_hash_len, const void *db_instance);
clabby's avatar
clabby committed
38
extern void rdb_free_string(char* string);
39
extern OpenDBResult open_db_read_only(const char *db_path);
clabby's avatar
clabby committed
40 41 42 43 44
*/
import "C"

// FetchRethReceipts fetches the receipts for the given block hash directly from the Reth Database
// and populates the given results slice pointer with the receipts that were found.
45
func FetchRethReceipts(db unsafe.Pointer, blockHash *common.Hash) (types.Receipts, error) {
clabby's avatar
clabby committed
46 47 48 49 50 51 52 53 54
	if blockHash == nil {
		return nil, fmt.Errorf("Must provide a block hash to fetch receipts for.")
	}

	// Convert the block hash to a C byte array and defer its deallocation
	cBlockHash := C.CBytes(blockHash[:])
	defer C.free(cBlockHash)

	// Call the C function to fetch the receipts from the Reth Database
55
	receiptsResult := C.rdb_read_receipts((*C.uint8_t)(cBlockHash), C.size_t(len(blockHash)), db)
clabby's avatar
clabby committed
56

clabby's avatar
clabby committed
57
	if receiptsResult.error {
clabby's avatar
clabby committed
58 59 60
		return nil, fmt.Errorf("Error fetching receipts from Reth Database.")
	}

clabby's avatar
clabby committed
61 62 63
	// Free the memory allocated by the C code
	defer C.rdb_free_string(receiptsResult.data)

clabby's avatar
clabby committed
64 65 66
	// Convert the returned JSON string to Go string and parse it
	receiptsJSON := C.GoStringN(receiptsResult.data, C.int(receiptsResult.data_len))
	var receipts types.Receipts
clabby's avatar
clabby committed
67 68 69
	if err := json.Unmarshal([]byte(receiptsJSON), &receipts); err != nil {
		return nil, err
	}
clabby's avatar
clabby committed
70

clabby's avatar
clabby committed
71
	return receipts, nil
clabby's avatar
clabby committed
72
}
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
func OpenDBReadOnly(dbPath string) (db unsafe.Pointer, err error) {
	// Convert the db path to a C string and defer its deallocation
	cDbPath := C.CString(dbPath)
	defer C.free(unsafe.Pointer(cDbPath))

	// Call the C function to fetch the receipts from the Reth Database
	openDBResult := C.open_db_read_only(cDbPath)

	if openDBResult.error {
		return nil, fmt.Errorf("failed to open RethDB")
	}

	return openDBResult.data, nil
}

89
type RethDBReceiptsFetcher struct {
90
	dbInstance unsafe.Pointer
91 92
}

93 94
var _ ReceiptsProvider = (*RethDBReceiptsFetcher)(nil)

95
// NewRethDBReceiptsFetcher opens a RethDB for reading receipts. It returns nil if it was unable to open the database
96
func NewRethDBReceiptsFetcher(dbPath string) *RethDBReceiptsFetcher {
97 98 99 100
	db, err := OpenDBReadOnly(dbPath)
	if err != nil {
		return nil
	}
101
	return &RethDBReceiptsFetcher{
102
		dbInstance: db,
103 104 105
	}
}

106
func (f *RethDBReceiptsFetcher) FetchReceipts(ctx context.Context, block eth.BlockInfo, txHashes []common.Hash) (types.Receipts, error) {
107 108 109
	if f.dbInstance == nil {
		return nil, fmt.Errorf("Reth dbInstance is nil")
	}
110
	hash := block.Hash()
111
	return FetchRethReceipts(f.dbInstance, &hash)
112 113 114 115 116 117 118 119 120 121 122 123 124 125
}

func NewCachingRethDBReceiptsFetcher(dbPath string, m caching.Metrics, cacheSize int) *CachingReceiptsProvider {
	return NewCachingReceiptsProvider(NewRethDBReceiptsFetcher(dbPath), m, cacheSize)
}

const buildRethdb = true

func newRecProviderFromConfig(client client.RPC, log log.Logger, metrics caching.Metrics, config *EthClientConfig) *CachingReceiptsProvider {
	if dbPath := config.RethDBPath; dbPath != "" {
		return NewCachingRethDBReceiptsFetcher(dbPath, metrics, config.ReceiptsCacheSize)
	}
	return newRPCRecProviderFromConfig(client, log, metrics, config)
}