Commit 42085ce9 authored by clabby's avatar clabby Committed by GitHub

chore(op-service): Remove `reth` receipts source (#11706)

parent 9452aa66
......@@ -1313,45 +1313,6 @@ jobs:
paths:
- "/go/pkg/mod"
op-service-rethdb-tests:
docker:
- image: <<pipeline.parameters.ci_builder_rust_image>>
steps:
- checkout
- check-changed:
patterns: op-service,op-node
- restore_cache:
name: Restore Go modules cache
key: gomod-{{ checksum "go.sum" }}
- run:
name: Cargo fmt + clippy
command: |
cargo +nightly fmt -- --check
cargo +nightly clippy --all --all-features -- -D warnings
working_directory: op-service/rethdb-reader
- run:
name: Generate testdata db
command: cargo test
working_directory: op-service/rethdb-reader
- run:
name: Build dylib
command: cargo build --release
working_directory: op-service/rethdb-reader
- run:
name: Update LD_LIBRARY_PATH
command: echo 'export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/root/project/op-service/rethdb-reader/target/release"' >> $BASH_ENV
- run:
name: Run op-service RethDB tests
command: |
gotestsum --format=standard-verbose --junitfile=/tmp/test-results/op-service.xml \
-- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out \
-run TestRethDB -tags rethdb -v
working_directory: op-service/sources
# TODO(CLI-148): Fix codecov
#- run:
#name: upload coverage
#command: codecov --verbose --clean --flags bedrock-rethdb-go-tests
bedrock-go-tests: # just a helper, that depends on all the actual test jobs
docker:
# Use a smaller base image to avoid pulling the huge ci-builder
......@@ -1622,9 +1583,6 @@ workflows:
requires:
- pnpm-monorepo
- cannon-prestate
- op-service-rethdb-tests:
requires:
- go-mod-download
- op-program-compat:
requires:
- op-program-tests
......@@ -1647,7 +1605,6 @@ workflows:
- op-program-compat
- op-service-tests
- op-supervisor-tests
- op-service-rethdb-tests
- op-e2e-HTTP-tests
- op-e2e-fault-proof-tests
- op-e2e-action-tests
......
......@@ -157,13 +157,6 @@ var (
}(),
Category: L1RPCCategory,
}
L1RethDBPath = &cli.StringFlag{
Name: "l1.rethdb",
Usage: "The L1 RethDB path, used to fetch receipts for L1 blocks.",
EnvVars: prefixEnvVars("L1_RETHDB"),
Hidden: true,
Category: L1RPCCategory,
}
L1RPCMaxConcurrency = &cli.IntFlag{
Name: "l1.max-concurrency",
Usage: "Maximum number of concurrent RPC requests to make to the L1 RPC provider.",
......@@ -413,7 +406,6 @@ var optionalFlags = []cli.Flag{
HeartbeatURLFlag,
RollupHalt,
RollupLoadProtocolVersions,
L1RethDBPath,
ConductorEnabledFlag,
ConductorRpcFlag,
ConductorRpcTimeoutFlag,
......
......@@ -65,9 +65,6 @@ type Config struct {
// Cancel to request a premature shutdown of the node itself, e.g. when halting. This may be nil.
Cancel context.CancelCauseFunc
// [OPTIONAL] The reth DB path to read receipts from
RethDBPath string
// Conductor is used to determine this node is the leader sequencer.
ConductorEnabled bool
ConductorRpc string
......
......@@ -172,9 +172,6 @@ func (n *OpNode) initL1(ctx context.Context, cfg *Config) error {
return fmt.Errorf("failed to get L1 RPC client: %w", err)
}
// Set the RethDB path in the EthClientConfig, if there is one configured.
rpcCfg.EthClientConfig.RethDBPath = cfg.RethDBPath
n.l1Source, err = sources.NewL1Client(
client.NewInstrumentedRPC(l1Node, &n.metrics.RPCMetrics.RPCClientMetrics), n.log, n.metrics.L1SourceCache, rpcCfg)
if err != nil {
......
......@@ -106,7 +106,6 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
SafeDBPath: ctx.String(flags.SafeDBPath.Name),
Sync: *syncConfig,
RollupHalt: haltOption,
RethDBPath: ctx.String(flags.L1RethDBPath.Name),
ConductorEnabled: ctx.Bool(flags.ConductorEnabledFlag.Name),
ConductorRpc: ctx.String(flags.ConductorRpcFlag.Name),
......
# Target
target/
# Bindings
rdb.h
# Testdata DB
testdata/db
This source diff could not be displayed because it is too large. You can view the blob instead.
[package]
name = "rethdb-reader"
description = "A simple library for reading data through Reth's DB abstractions."
version = "0.1.0"
edition = "2021"
[lib]
name = "rethdbreader"
crate-type = ["cdylib"]
[dependencies]
# reth
reth-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
reth-provider = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
reth-db = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
reth-rpc-types = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
reth-blockchain-tree = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
# misc
serde = "1.0.197"
serde_json = "1.0.114"
anyhow = "1.0.80"
[dev-dependencies]
reth-revm = { git = "https://github.com/paradigmxyz/reth.git", rev = "e0c220e" }
alloy-rlp = "0.3.4"
.PHONY: testdata
testdata:
mkdir -p testdata
@echo "Fetching block RLP and receipts for block #18,663,292 from ethereum mainnet"
cast rpc debug_getRawBlock 0x11CC77C | jq -r | xxd -r -p > testdata/block.rlp
cast rpc debug_getRawReceipts 0x11CC77C | jq -r > testdata/receipts.json
@echo "Done. Generating testdata DB & testing integrity..."
cargo test
# `rethdb-reader`
A dylib to be accessed via FFI in `op-service`'s `sources` package for reading information
directly from the `reth` database.
## Developing
**Building**
To build the dylib, you must first have the [Rust Toolchain][rust-toolchain] installed.
```sh
cargo build --release
```
**Docs**
Documentation is available via rustdoc.
```sh
cargo doc --open
```
**Linting**
```sh
cargo +nightly fmt -- && cargo +nightly clippy --all --all-features -- -D warnings
```
**Generating `testdata`**
The testdata block and `reth` database can be generated by running the `testdata` make directive:
```sh
ETH_RPC_URL="<your_L1_RPC_URL>" make testdata
```
**Generating the C header**
To generate the C header, first install `cbindgen` via `cargo install cbindgen --force`. Then, run the generation script:
```sh
./headgen.sh
```
### C Header
The C header below is generated by `cbindgen`, and it is the interface that consumers of the dylib use to call its exported
functions. Currently, the only exported functions pertain to reading fully hydrated block receipts from the database.
```c
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/**
* A [ReceiptsResult] is a wrapper around a JSON string containing serialized [TransactionReceipt]s
* as well as an error status that is compatible with FFI.
*
* # Safety
* - When the `error` field is false, the `data` pointer is guaranteed to be valid.
* - When the `error` field is true, the `data` pointer is guaranteed to be null.
*/
typedef struct ReceiptsResult {
uint32_t *data;
uintptr_t data_len;
bool error;
} ReceiptsResult;
/**
* A [OpenDBResult] is a wrapper of DB instance [BlockchainProvider]
* as well as an error status that is compatible with FFI.
*
* # Safety
* - When the `error` field is false, the `data` pointer is guaranteed to be valid.
* - When the `error` field is true, the `data` pointer is guaranteed to be null.
*/
typedef struct OpenDBResult {
const void *data;
bool error;
} OpenDBResult;
/**
* Read the receipts for a blockhash from the RETH database directly.
*
* # Safety
* - All possible nil pointer dereferences are checked, and the function will return a failing
* [ReceiptsResult] if any are found.
*/
struct ReceiptsResult rdb_read_receipts(const uint8_t *block_hash,
uintptr_t block_hash_len,
const void *db_instance);
/**
* Free a string that was allocated in Rust and passed to C.
*
* # Safety
* - All possible nil pointer dereferences are checked.
*/
void rdb_free_string(char *string);
/**
* Open a DB instance and return.
*
* # Safety
* - All possible nil pointer dereferences are checked, and the function will return a failing
* [OpenDBResult] if any are found.
*/
struct OpenDBResult open_db_read_only(const char *db_path);
```
[rust-toolchain]: https://rustup.rs/
#!/bin/bash
set -e
# Generate rdb.h
cbindgen --crate rethdb-reader --output rdb.h -l C
# Process README.md to replace the content within the specified code block
awk '
BEGIN { in_code_block=0; }
/^```c/ { in_code_block=1; print; next; }
/^```/ && in_code_block { in_code_block=0; while ((getline line < "rdb.h") > 0) print line; }
!in_code_block { print; }
' README.md > README.tmp && mv README.tmp README.md
echo "Generated C header successfully"
reorder_imports = true
imports_granularity = "Crate"
use_small_heuristics = "Max"
comment_width = 100
wrap_comments = true
binop_separator = "Back"
trailing_comma = "Vertical"
trailing_semicolon = false
use_field_init_shorthand = true
format_code_in_doc_comments = true
doc_comment_code_block_width = 100
use anyhow::{anyhow, Result};
use reth_blockchain_tree::noop::NoopBlockchainTree;
use reth_db::{mdbx::DatabaseArguments, open_db_read_only};
use reth_primitives::MAINNET;
use reth_provider::{providers::BlockchainProvider, ProviderFactory};
use std::{
ffi::{c_char, c_void},
path::Path,
};
/// A [OpenDBResult] is a wrapper of DB instance [BlockchainProvider]
/// as well as an error status that is compatible with FFI.
///
/// # Safety
/// - When the `error` field is false, the `data` pointer is guaranteed to be valid.
/// - When the `error` field is true, the `data` pointer is guaranteed to be null.
#[repr(C)]
pub struct OpenDBResult {
pub(crate) data: *const c_void,
pub(crate) error: bool,
}
impl OpenDBResult {
/// Constructs a successful [OpenDBResult] from a DB instance.
pub fn success(data: *const c_void) -> Self {
Self { data, error: false }
}
/// Constructs a failing [OpenDBResult] with a null pointer to the data.
pub fn fail() -> Self {
Self { data: std::ptr::null_mut(), error: true }
}
}
/// Open and return a DB instance.
///
/// # Safety
/// - All possible nil pointer dereferences are checked, and the function will return a failing
/// [OpenDBResult] if any are found.
#[inline(always)]
pub(crate) unsafe fn open_db_read_only_inner(db_path: *const c_char) -> Result<OpenDBResult> {
// Convert the *const c_char to a Rust &str
let db_path_str = {
if db_path.is_null() {
anyhow::bail!("db path pointer is null");
}
std::ffi::CStr::from_ptr(db_path)
}
.to_str()?;
let db = open_db_read_only(Path::new(db_path_str), DatabaseArguments::default())
.map_err(|e| anyhow!(e))?;
let factory = ProviderFactory::new(db, MAINNET.clone());
// Create a read-only BlockChainProvider
let provider = Box::new(BlockchainProvider::new(factory, NoopBlockchainTree::default())?);
let res = OpenDBResult::success(Box::into_raw(provider) as *const c_void);
Ok(res)
}
#![doc = include_str!("../README.md")]
use db::{open_db_read_only_inner, OpenDBResult};
use receipts::{read_receipts_inner, ReceiptsResult};
use std::ffi::c_void;
use std::os::raw::c_char;
mod db;
mod receipts;
/// Read the receipts for a blockhash from the RETH database directly.
///
/// # Safety
/// - All possible nil pointer dereferences are checked, and the function will return a failing
/// [ReceiptsResult] if any are found.
#[no_mangle]
pub unsafe extern "C" fn rdb_read_receipts(
block_hash: *const u8,
block_hash_len: usize,
db_instance: *const c_void,
) -> ReceiptsResult {
read_receipts_inner(block_hash, block_hash_len, db_instance).unwrap_or(ReceiptsResult::fail())
}
/// Free a string that was allocated in Rust and passed to C.
///
/// # Safety
/// - All possible nil pointer dereferences are checked.
#[no_mangle]
pub unsafe extern "C" fn rdb_free_string(string: *mut c_char) {
// Convert the raw pointer back to a CString and let it go out of scope,
// which will deallocate the memory.
if !string.is_null() {
let _ = std::ffi::CString::from_raw(string);
}
}
/// Open a DB instance and return.
///
/// # Safety
/// - All possible nil pointer dereferences are checked, and the function will return a failing
/// [OpenDBResult] if any are found.
#[no_mangle]
pub unsafe extern "C" fn open_db_read_only(db_path: *const c_char) -> OpenDBResult {
open_db_read_only_inner(db_path).unwrap_or(OpenDBResult::fail())
}
//! This module contains the logic for reading a block's fully hydrated receipts directly from the
//! [reth] database.
use anyhow::{anyhow, Result};
use reth_blockchain_tree::noop::NoopBlockchainTree;
use reth_db::DatabaseEnv;
use reth_primitives::{
BlockHashOrNumber, Receipt, TransactionKind, TransactionMeta, TransactionSigned, U128, U256,
U64,
};
use reth_provider::{providers::BlockchainProvider, BlockReader, ReceiptProvider};
use reth_rpc_types::{Log, TransactionReceipt};
use std::ffi::c_void;
/// A [ReceiptsResult] is a wrapper around a JSON string containing serialized [TransactionReceipt]s
/// as well as an error status that is compatible with FFI.
///
/// # Safety
/// - When the `error` field is false, the `data` pointer is guaranteed to be valid.
/// - When the `error` field is true, the `data` pointer is guaranteed to be null.
#[repr(C)]
pub struct ReceiptsResult {
data: *mut char,
data_len: usize,
error: bool,
}
impl ReceiptsResult {
/// Constructs a successful [ReceiptsResult] from a JSON string.
pub fn success(data: *mut char, data_len: usize) -> Self {
Self { data, data_len, error: false }
}
/// Constructs a failing [ReceiptsResult] with a null pointer to the data.
pub fn fail() -> Self {
Self { data: std::ptr::null_mut(), data_len: 0, error: true }
}
}
/// Read the receipts for a blockhash from the RETH database directly.
///
/// # Safety
/// - All possible nil pointer dereferences are checked, and the function will return a failing
/// [ReceiptsResult] if any are found.
#[inline(always)]
pub(crate) unsafe fn read_receipts_inner(
block_hash: *const u8,
block_hash_len: usize,
db_instance: *const c_void,
) -> Result<ReceiptsResult> {
// Convert the raw pointer and length back to a Rust slice
let block_hash: [u8; 32] = {
if block_hash.is_null() {
anyhow::bail!("block_hash pointer is null");
}
std::slice::from_raw_parts(block_hash, block_hash_len)
}
.try_into()?;
// Create a read-only BlockChainProvider
let provider = &*(db_instance as *const BlockchainProvider<DatabaseEnv, NoopBlockchainTree>);
// Fetch the block and the receipts within it
let block =
provider.block_by_hash(block_hash.into())?.ok_or(anyhow!("Failed to fetch block"))?;
let receipts = provider
.receipts_by_block(BlockHashOrNumber::Hash(block_hash.into()))?
.ok_or(anyhow!("Failed to fetch block receipts"))?;
let block_number = block.number;
let base_fee = block.base_fee_per_gas;
let block_hash = block.hash_slow();
let receipts = block
.body
.into_iter()
.zip(receipts.clone())
.enumerate()
.map(|(idx, (tx, receipt))| {
let meta = TransactionMeta {
tx_hash: tx.hash,
index: idx as u64,
block_hash,
block_number,
base_fee,
excess_blob_gas: None,
};
build_transaction_receipt_with_block_receipts(tx, meta, receipt, &receipts)
})
.collect::<Option<Vec<_>>>()
.ok_or(anyhow!("Failed to build receipts"))?;
// Convert the receipts to JSON for transport
let mut receipts_json = serde_json::to_string(&receipts)?;
// Create a ReceiptsResult with a pointer to the json-ified receipts
let res = ReceiptsResult::success(receipts_json.as_mut_ptr() as *mut char, receipts_json.len());
// Forget the `receipts_json` string so that its memory isn't freed by the
// borrow checker at the end of this scope
std::mem::forget(receipts_json); // Prevent Rust from freeing the memory
Ok(res)
}
/// Builds a hydrated [TransactionReceipt] from information in the passed transaction,
/// receipt, and block receipts.
///
/// Returns [None] if the transaction's sender could not be recovered from the signature.
#[inline(always)]
fn build_transaction_receipt_with_block_receipts(
tx: TransactionSigned,
meta: TransactionMeta,
receipt: Receipt,
all_receipts: &[Receipt],
) -> Option<TransactionReceipt> {
let transaction = tx.clone().into_ecrecovered()?;
// get the previous transaction cumulative gas used
let gas_used = if meta.index == 0 {
receipt.cumulative_gas_used
} else {
let prev_tx_idx = (meta.index - 1) as usize;
all_receipts
.get(prev_tx_idx)
.map(|prev_receipt| receipt.cumulative_gas_used - prev_receipt.cumulative_gas_used)
.unwrap_or_default()
};
let mut res_receipt = TransactionReceipt {
transaction_hash: Some(meta.tx_hash),
transaction_index: U64::from(meta.index),
block_hash: Some(meta.block_hash),
block_number: Some(U256::from(meta.block_number)),
from: transaction.signer(),
to: None,
cumulative_gas_used: U256::from(receipt.cumulative_gas_used),
gas_used: Some(U256::from(gas_used)),
contract_address: None,
logs: Vec::with_capacity(receipt.logs.len()),
effective_gas_price: U128::from(transaction.effective_gas_price(meta.base_fee)),
transaction_type: tx.transaction.tx_type().into(),
// TODO pre-byzantium receipts have a post-transaction state root
state_root: None,
logs_bloom: receipt.bloom_slow(),
status_code: if receipt.success { Some(U64::from(1)) } else { Some(U64::from(0)) },
// EIP-4844 fields
blob_gas_price: None,
blob_gas_used: None,
// Other:
other: Default::default(),
};
match tx.transaction.kind() {
TransactionKind::Create => {
res_receipt.contract_address =
Some(transaction.signer().create(tx.transaction.nonce()));
}
TransactionKind::Call(addr) => {
res_receipt.to = Some(*addr);
}
}
// get number of logs in the block
let mut num_logs = 0;
for prev_receipt in all_receipts.iter().take(meta.index as usize) {
num_logs += prev_receipt.logs.len();
}
for (tx_log_idx, log) in receipt.logs.into_iter().enumerate() {
let rpclog = Log {
address: log.address,
topics: log.topics,
data: log.data,
block_hash: Some(meta.block_hash),
block_number: Some(U256::from(meta.block_number)),
transaction_hash: Some(meta.tx_hash),
transaction_index: Some(U256::from(meta.index)),
log_index: Some(U256::from(num_logs + tx_log_idx)),
removed: false,
};
res_receipt.logs.push(rpclog);
}
Some(res_receipt)
}
#[cfg(test)]
mod test {
use super::*;
use alloy_rlp::Decodable;
use reth_db::{database::Database, mdbx::DatabaseArguments};
use reth_primitives::{
address, b256, bloom, hex, Address, Block, Bytes, ReceiptWithBloom, Receipts,
SealedBlockWithSenders, MAINNET, U8,
};
use reth_provider::{BlockWriter, BundleStateWithReceipts, DatabaseProvider};
use reth_revm::revm::db::BundleState;
use std::{
ffi::{c_char, CString},
fs::File,
path::Path,
};
#[inline]
fn dummy_block_with_receipts() -> Result<(Block, Vec<Receipt>)> {
// To generate testdata (block 18,663,292 on Ethereum Mainnet):
// 1. BLOCK RLP: `cast rpc debug_getRawBlock 0x11CC77C | jq -r | xxd -r -p >
// testdata/block.rlp`
// 2. RECEIPTS RLP: `cast rpc debug_getRawReceipts 0x11CC77C | jq -r >
// testdata/receipts.json`
let block_rlp = include_bytes!("../testdata/block.rlp");
let block = Block::decode(&mut block_rlp.as_ref())?;
let receipt_rlp: Vec<Vec<u8>> = serde_json::from_str(include_str!(
"../testdata/receipts.json"
))
.map(|v: Vec<String>| {
v.into_iter().map(|s| hex::decode(s)).collect::<Result<Vec<Vec<u8>>, _>>()
})??;
let receipts = receipt_rlp
.iter()
.map(|r| ReceiptWithBloom::decode(&mut r.as_slice()).map(|r| r.receipt))
.collect::<Result<Vec<Receipt>, _>>()?;
Ok((block, receipts))
}
#[inline]
fn open_receipts_testdata_db() -> Result<()> {
if File::open("testdata/db").is_ok() {
return Ok(());
}
// Open a RW handle to the MDBX database
let db = reth_db::init_db(Path::new("testdata/db"), DatabaseArguments::default())
.map_err(|e| anyhow!(e))?;
let pr = DatabaseProvider::new_rw(db.tx_mut()?, MAINNET.clone());
// Grab the dummy block and receipts
let (mut block, receipts) = dummy_block_with_receipts()?;
// Patch: The block's current state root expects the rest of the chain history to be in the
// DB; manually override it. Otherwise, the DatabaseProvider will fail to commit the
// block.
block.header.state_root = reth_primitives::constants::EMPTY_ROOT_HASH;
// Fetch the block number and tx senders for bundle state creation.
let block_number = block.header.number;
let senders = block
.body
.iter()
.map(|tx| tx.recover_signer())
.collect::<Option<Vec<Address>>>()
.ok_or(anyhow!("Failed to recover signers"))?;
// Commit the bundle state to the database
pr.append_blocks_with_state(
vec![SealedBlockWithSenders { block: block.seal_slow(), senders }],
BundleStateWithReceipts::new(
BundleState::default(),
Receipts::from_block_receipt(receipts),
block_number,
),
Default::default(),
Default::default(),
None,
)?;
pr.commit()?;
Ok(())
}
#[test]
fn fetch_receipts() {
open_receipts_testdata_db().unwrap();
unsafe {
let res = crate::open_db_read_only(
CString::new("testdata/db").unwrap().into_raw() as *const c_char
);
assert_eq!(res.error, false);
let mut block_hash =
b256!("6a229123d607c2232a8b0bdd36f90745945d05181018e64e60ff2b93ab6b52e5");
let receipts_res =
super::read_receipts_inner(block_hash.as_mut_ptr(), 32, res.data).unwrap();
let receipts_data =
std::slice::from_raw_parts(receipts_res.data as *const u8, receipts_res.data_len);
let receipt = {
let mut receipts: Vec<TransactionReceipt> =
serde_json::from_slice(receipts_data).unwrap();
receipts.remove(0)
};
// Check the first receipt in the block for validity
assert_eq!(receipt.transaction_type, U8::from(2));
assert_eq!(receipt.status_code, Some(U64::from(1)));
assert_eq!(receipt.cumulative_gas_used, U256::from(115_316));
assert_eq!(receipt.logs_bloom, bloom!("00200000000000000000000080001000000000000000000000000000000000000000000000000000000000000000100002000100080000000000000000000000000000000000000000000008000000200000000400000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000400000000000001000000000000000100000000080000004000000000000000000000000000000000000002000000000000000000000000000000000000000006000000000000000000000000000000000000001000000000000000000000200000000000000100000000020000000000000000000000000000000010"));
assert_eq!(
receipt.logs[0].address,
address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")
);
assert_eq!(
receipt.logs[0].topics[0],
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
);
assert_eq!(
receipt.logs[0].topics[1],
b256!("00000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40")
);
assert_eq!(
receipt.logs[0].data,
Bytes::from_static(
hex!("00000000000000000000000000000000000000000000000008a30cd230000000")
.as_slice()
)
);
assert_eq!(receipt.from, address!("41d3ab85aafed2ef9e644cb7d3bbca2fc4d8cac8"));
assert_eq!(receipt.to, Some(address!("00000000003b3cc22af3ae1eac0440bcee416b40")));
assert_eq!(
receipt.transaction_hash,
Some(b256!("88b2d153a4e893ba91ac235325c44b1aa0c802fcb42657701e1a73e1c675f7ca"))
);
assert_eq!(receipt.block_number, Some(U256::from(18_663_292)));
assert_eq!(receipt.block_hash, Some(block_hash));
assert_eq!(receipt.transaction_index, U64::from(0));
crate::rdb_free_string(receipts_res.data as *mut c_char);
}
}
}
[
"0xb903c202f903be018301c274b9010000200000000000000000000080001000000000000000000000000000000000000000000000000000000000000000100002000100080000000000000000000000000000000000000000000008000000200000000400000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000400000000000001000000000000000100000000080000004000000000000000000000000000000000000002000000000000000000000000000000000000000006000000000000000000000000000000000000001000000000000000000000200000000000000100000000020000000000000000000000000000000010f902b3f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa000000000000000000000000000000000000000000000000008a30cd230000000f89b94d10bc8517059e51b625e1e57a60b348d552f685df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a000000000000000000000000000000000000000000000000000007921e915b684f879945c1694c8168668c11777909c79142dc9d992788fe1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b840000000000000000000000000000000000000000000000001129c9861392306df000000000000000000000000000000000000000000000000000e9d834abd9cdff8fc945c1694c8168668c11777909c79142dc9d992788ff863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40b88000000000000000000000000000000000000000000000000008a30cd2300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007921e915b684",
"0xb9043e02f9043a0183042135b9010000200000000000000000000080001000100000000000000000000000000000000000000000000000000000000000100002000000080000000000000000010000000000000000010000000008000000200000000400100000000000008000000000000000000000000000000000000000000000000000000000000010000000000000000400000000000001000000000000000101000000080000004000000000000000000000000000000000000002000000000000000000000000000000000000000002000000000000000000000000000000000000001000000000000000000000200000000000008000000000020000000000000000400000000000000010f9032ff87a94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f842a0e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109ca00000000000000000000000002ec705d306b51e486b1bc0d6ebee708e0661add1a00000000000000000000000000000000000000000000000000bd0c7ca01c48000f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000002ec705d306b51e486b1bc0d6ebee708e0661add1a00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa00000000000000000000000000000000000000000000000000bd0c7ca01c48000f89b94d10bc8517059e51b625e1e57a60b348d552f685df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa00000000000000000000000001f3c8f19ab956457994de1d50eb0ae7d057f77f8a0000000000000000000000000000000000000000000000000000099e622c10336f879945c1694c8168668c11777909c79142dc9d992788fe1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b8400000000000000000000000000000000000000000000000011e6d602b3ae786df000000000000000000000000000000000000000000000000000e039d27fc99a9f8fc945c1694c8168668c11777909c79142dc9d992788ff863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a00000000000000000000000002ec705d306b51e486b1bc0d6ebee708e0661add1a00000000000000000000000001f3c8f19ab956457994de1d50eb0ae7d057f77f8b8800000000000000000000000000000000000000000000000000bd0c7ca01c4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099e622c10336",
"0xb903c202f903be018305b26fb9010000200000000000000000000080001000000000000000000000000000000000000000000000000000000000000000100002000100080000000000000000000000000000000000000000000008000000200000000400000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000400000000000001000000000000000100000000080000004000000000000000000000000000000000000002000000000000000000000000000000000000000006000000000000000000000000000000000000001000000000000000000000200000000000000100000000020000000000000000000000000000000010f902b3f89b94d10bc8517059e51b625e1e57a60b348d552f685df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa000000000000000000000000000000000000000000000000000007921e99216a0f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000005c1694c8168668c11777909c79142dc9d992788fa000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a00000000000000000000000000000000000000000000000000953f38350000000f879945c1694c8168668c11777909c79142dc9d992788fe1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b84000000000000000000000000000000000000000000000000115196ca7eae786df000000000000000000000000000000000000000000000000000e7cbf118eb049f8fc945c1694c8168668c11777909c79142dc9d992788ff863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40a000000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40b880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007921e99216a00000000000000000000000000000000000000000000000000953f383500000000000000000000000000000000000000000000000000000000000000000000000",
"0xb90bdf02f90bdb01830b2215b9010000200000002000000000000080000000000000000000000001010000000004000000000000000000420000000000010002000000080020002000000000200800000000000000080800000008000000200090000000000000080000408040000000000000000000000008084000000000000000000000000010010050000800000800800000000000000000000000000000000000000000080000004000100000020100000000000000100080000004000000000000020000000000000000000000400002000000000000000000000000000000000000001000000000002000400012200000800000020404000000000000020000008000000000000000000010f90ad0f89b941111111254eeb25477b68fb85ed929f73a960582f842a0b9ed0243fdf00f0545c63a0af8850c090d86bb46682baec4bf3c496814fe4f02a000000000000000000000000058c1c6a484ef2d6b0f8d93b2dbeb72f3c3e9ceb5b8402511106c0eade4c289445ac7275a89e6e088cc2cb5e313a42712b2987c2fb8d30000000000000000000000000000000000000000000000000000000000000001f89b94dac17f958d2ee523a2206206994597c13d831ec7f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000058c1c6a484ef2d6b0f8d93b2dbeb72f3c3e9ceb5a0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa000000000000000000000000000000000000000000000000000000002f0174a58f89b94fe67a4450907459c3e1fff623aa927dd4e28c67af863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000a050f7adc241afa59ad5f343eaf4faf5f30a4cdba0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa000000000000000000000000000000000000000000000143c3719a34f04e020edf89b94dac17f958d2ee523a2206206994597c13d831ec7f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa000000000000000000000000006da0fd433c1a5d7a4faa01111c044910a184553a000000000000000000000000000000000000000000000000000000000da1b3bf5f89b94dac17f958d2ee523a2206206994597c13d831ec7f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa00000000000000000000000000d4a11d5eeaac28ec3f61d100daf4d40471f1852a00000000000000000000000000000000000000000000000000000000215fc0e63f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000006da0fd433c1a5d7a4faa01111c044910a184553a0000000000000000000000000a050f7adc241afa59ad5f343eaf4faf5f30a4cdba00000000000000000000000000000000000000000000000001913572822ee731ff8799406da0fd433c1a5d7a4faa01111c044910a184553e1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b8400000000000000000000000000000000000000000000000b3986c20be3ddb96f70000000000000000000000000000000000000000000000000000061647f821daf8fc9406da0fd433c1a5d7a4faa01111c044910a184553f863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa0000000000000000000000000a050f7adc241afa59ad5f343eaf4faf5f30a4cdbb880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da1b3bf50000000000000000000000000000000000000000000000001913572822ee731f0000000000000000000000000000000000000000000000000000000000000000f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000000d4a11d5eeaac28ec3f61d100daf4d40471f1852a0000000000000000000000000a050f7adc241afa59ad5f343eaf4faf5f30a4cdba00000000000000000000000000000000000000000000000003d5ec06d1b711bcff879940d4a11d5eeaac28ec3f61d100daf4d40471f1852e1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b8400000000000000000000000000000000000000000000004724d25b9c0c67a0c7800000000000000000000000000000000000000000000000000002694ca870b04f8fc940d4a11d5eeaac28ec3f61d100daf4d40471f1852f863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa0000000000000000000000000a050f7adc241afa59ad5f343eaf4faf5f30a4cdbb88000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215fc0e630000000000000000000000000000000000000000000000003d5ec06d1b711bcf0000000000000000000000000000000000000000000000000000000000000000f9011c94a050f7adc241afa59ad5f343eaf4faf5f30a4cdbf863a0c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781ab8a0000000000000000000000000000000000000000000000000567217953e5f8eeeffffffffffffffffffffffffffffffffffffffffffffebc3c8e65cb0fb1fdf13000000000000000000000000000000000000007b89284efd1b25e6f5f0dcbf2e000000000000000000000000000000000000000000000aa86d459fd0c59c2a59000000000000000000000000000000000000000000000000000000000001784ff89b94fe67a4450907459c3e1fff623aa927dd4e28c67af863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000d1742b3c4fbb096990c8950fa635aec75b30781aa0000000000000000000000000a88800cd213da5ae406ce248380802bd53b47647a0000000000000000000000000000000000000000000001375a0d26e739f71f370f89b94fe67a4450907459c3e1fff623aa927dd4e28c67af863a08c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a0000000000000000000000000a88800cd213da5ae406ce248380802bd53b47647a00000000000000000000000001111111254eeb25477b68fb85ed929f73a960582a0000000000000000000000000000000000000000000001375a0d26e739f71f370f89b94fe67a4450907459c3e1fff623aa927dd4e28c67af863a08c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a0000000000000000000000000a88800cd213da5ae406ce248380802bd53b47647a00000000000000000000000001111111254eeb25477b68fb85ed929f73a960582a00000000000000000000000000000000000000000000000000000000000000000f89b94fe67a4450907459c3e1fff623aa927dd4e28c67af863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000a88800cd213da5ae406ce248380802bd53b47647a000000000000000000000000058c1c6a484ef2d6b0f8d93b2dbeb72f3c3e9ceb5a0000000000000000000000000000000000000000000001375a0d26e739f71f370",
"0xb905cf02f905cb01830e0902b9010000200000008020000000000280004000000000000000000000000000000000000000200000000000000000200080000002000000080000000000000800000000000000000000000100000008040000200004000000000000000000008000004000000000000000000000000000000400240000000000000004000010000000000000000000000000000001010000000200000001000000080000004000000400002000000000004002020000000000000000000000000000002000000000000000000002000000000000000000000040000000000000001000000000000000000000200000000000000000000000000000000001000400400000000080010000f904c0f87a94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f842a0e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109ca0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216a000000000000000000000000000000000000000000000000001617eb90b26c000f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216a0000000000000000000000000e090e0518ea17607a01e9286522655808c3f8919a000000000000000000000000000000000000000000000000001617eb90b26c000f89b94f83e0399fd7ef1c9c1a3dbc0a4d586edc3c8d125f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000e090e0518ea17607a01e9286522655808c3f8919a0000000000000000000000000faa04b4d06297dca749c2ac7783211638de3e491a000000000000000000000000000000000000000000000019d0da576764721e7e7f87994e090e0518ea17607a01e9286522655808c3f8919e1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b8400000000000000000000000000000000000000000000000001e6d6c84d2f7cd2b00000000000000000000000000000000000000000000220ae8d75c1df124f53df8fc94e090e0518ea17607a01e9286522655808c3f8919f863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a0000000000000000000000000c6265979793435b496e28e61af1500c22c3ba277a0000000000000000000000000faa04b4d06297dca749c2ac7783211638de3e491b88000000000000000000000000000000000000000000000000001617eb90b26c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019d0da576764721e7e7f87994db5889e35e379ef0498aae126fc2cce1fbd23216e1a06cb8c9cbcedb37b7d125170f5d929dbbdb3203c8fd4d60356b496fa31f77fd00b8400000000000000000000000000000000000000000000000000001c4793ec69800000000000000000000000000000000000000000000000000000000000000008ef87994db5889e35e379ef0498aae126fc2cce1fbd23216e1a00c2a2f565c7774c59e49ef6b3c255329f4d254147e06e724d3a8569bb7bd21adb8400000000000000000000000000000000000000000000000000001c4793ec69800000000000000000000000000faa04b4d06297dca749c2ac7783211638de3e491f89994db5889e35e379ef0498aae126fc2cce1fbd23216e1a09f849d23f4955d98202378ea318f2b0c7533695d3c9fb2a3931f0f919fa8c420b86000000000000000000000000000000000000000000000000001617eb90b26c00000000000000000000000000000000000000000000000019d0da576764721e7e700000000000000000000000000000000000000000000019d0da576764721e7e7",
"0xf905cb0183105d5cb90100000000000180a0000004000000004000000000000001000000000000002200000000000000000000080200200000000002000000080020000000000800000000000000000000000900000008040000000000000000400000000000000000004000000000000000000000000000000400240000000000040000000010000804000000000000000000000080010000000000000000000000008000000000000480000000000000004002000000000000000000000000000000000000000000008000000002080000000000000000000000000000000000000000000002000000000000200000000000000000000000000000000000000000000000000000010000f904c0f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000009e31efffe1e016f514f774f43d91a85ebbc11564a0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216a00000000000000000000000000000000000000000000000000e803e90becaf29bf89b949dd5f5dae94633760b3125a7490ecb4571d09a42f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000087033ad96757ecd730aa1652b1a7dabcc5ffb182a00000000000000000000000009e31efffe1e016f514f774f43d91a85ebbc11564a0000000000000000000000000000000000000000000acb37f48fe24c23d1833c6f9011c949e31efffe1e016f514f774f43d91a85ebbc11564f863a0c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216a0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216b8a0000000000000000000000000000000000000000000acb37f48fe24c23d1833c6fffffffffffffffffffffffffffffffffffffffffffffffff17fc16f41350d65000000000000000000000000000000000000000000049c65546ad3eed1521a58000000000000000000000000000000000000000000009404a8bb774136003dadfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd14eff87a94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f842a07fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65a0000000000000000000000000db5889e35e379ef0498aae126fc2cce1fbd23216a00000000000000000000000000000000000000000000000000e803e90becaf29bf87994db5889e35e379ef0498aae126fc2cce1fbd23216e1a06cb8c9cbcedb37b7d125170f5d929dbbdb3203c8fd4d60356b496fa31f77fd00b84000000000000000000000000000000000000000000000000000128fac3e64dad00000000000000000000000000000000000000000000000000000000000000012f87994db5889e35e379ef0498aae126fc2cce1fbd23216e1a00c2a2f565c7774c59e49ef6b3c255329f4d254147e06e724d3a8569bb7bd21adb84000000000000000000000000000000000000000000000000000128fac3e64dad000000000000000000000000087033ad96757ecd730aa1652b1a7dabcc5ffb182f89994db5889e35e379ef0498aae126fc2cce1fbd23216e1a09f849d23f4955d98202378ea318f2b0c7533695d3c9fb2a3931f0f919fa8c420b860000000000000000000000000000000000000000000acb37f48fe24c23d1833c60000000000000000000000000000000000000000000000000e803e90becaf29b0000000000000000000000000000000000000000000000000e803e90becaf29bf85894db5889e35e379ef0498aae126fc2cce1fbd23216e1a0522881958b3c4a6fc0840ad3b7fb947b881edc28c004245a62541647422ade97a00000000000000000000000000000000000000000000000000e803e90becaf29b",
"0xb903e402f903e00183128fdcb9010000000000010000000000000000000000000000000000010000000000040000000000000000000000000008000000000002000000080020000000000000000000000000000000000808000008000000000000000000410000000000000000000000000000000000000000040000000000000000000000040000000010000800000000000000000000200000000000000000000000010000000000000000000000000000000000200000000000010000000000000000000000002000000008000000000002000000000000080000000000000000000000000000000002000000000000200000000000000010000000000000000000000000000000000000000000f902d5f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640a00000000000000000000000008e30dead12d19228cf9cdc984f237f0ad00df195a000000000000000000000000000000000000000000000000000b5762b8f483dbff89b94a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000f706190050f8c7d62021d5ba52871640ea9fd3fea000000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640a0000000000000000000000000000000000000000000000000000000000629852ff9011c9488e6a0c2ddd26feeb64f039a2c41296fcb3f5640f863a0c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a00000000000000000000000008e30dead12d19228cf9cdc984f237f0ad00df195a00000000000000000000000008e30dead12d19228cf9cdc984f237f0ad00df195b8a0000000000000000000000000000000000000000000000000000000000629852fffffffffffffffffffffffffffffffffffffffffffffffffff4a89d470b7c24100000000000000000000000000000000000056d8c2fe7c57e75b18c386394ff90000000000000000000000000000000000000000000000016397a5e7cf05707c0000000000000000000000000000000000000000000000000000000000030e04f87a94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f842a07fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65a00000000000000000000000008e30dead12d19228cf9cdc984f237f0ad00df195a000000000000000000000000000000000000000000000000000b5762b8f483dbf",
"0xb901ab02f901a701831345f7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000200000000000000000000000004000000000008000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000080000000000000008000000000000000000000000000000000000000000000000100000000000000000000010000000000000000000000001000000000000000000000000000000000000f89df89b9496add417293a49e80f024734e96cfd8b355bcc14f863a08c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a0000000000000000000000000af56ec1d3a54e9d05fbe2bc158219a810890dc77a0000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xb9053d02f90539018315a059b9010000210000000000100000100080000000000000000000000080000000000000000000000000000000000000000000000002010000080000000000000000080000000020080000800000000008000000208000000000400000000000000020000000000000000000000000000000000000000000000000040000000010000000000040004000000000000000000000000000000000000000080000004000000000000000000000000000000000000000000400000000080000000000000008000000000002000000000000000000000000000400400000101000000002000202000000200000000000000000000001000000001000000000000000000000000400f9042ef8fd94000000000022d473030f116ddee9f6b43ac78ba3f884a0c6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708eca0000000000000000000000000af56ec1d3a54e9d05fbe2bc158219a810890dc77a000000000000000000000000096add417293a49e80f024734e96cfd8b355bcc14a00000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fadb860000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000658c27210000000000000000000000000000000000000000000000000000000000000000f89b9496add417293a49e80f024734e96cfd8b355bcc14f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa0000000000000000000000000af56ec1d3a54e9d05fbe2bc158219a810890dc77a00000000000000000000000000bee64a7c035fa2f69b9a6cad5d761de038ecb02a000000000000000000000000000000000000000000000000c83b84b800fe98c96f89b94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa00000000000000000000000000bee64a7c035fa2f69b9a6cad5d761de038ecb02a00000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fada000000000000000000000000000000000000000000000000001e7841cb75e9302f879940bee64a7c035fa2f69b9a6cad5d761de038ecb02e1a01c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1b840000000000000000000000000000000000000000000004581511bfd43276c16b400000000000000000000000000000000000000000000000a99e131364ae486cff8fc940bee64a7c035fa2f69b9a6cad5d761de038ecb02f863a0d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822a00000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fada00000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fadb88000000000000000000000000000000000000000000000000c83b84b800fe98c960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e7841cb75e9302f87a94c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f842a07fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65a00000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fada000000000000000000000000000000000000000000000000001e7841cb75e9302",
"0xb9010d02f90109018315f261b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0"
]
......@@ -11,6 +11,7 @@ package sources
import (
"context"
"errors"
"fmt"
"math/big"
"time"
......@@ -62,11 +63,6 @@ type EthClientConfig struct {
// till we re-attempt the user-preferred methods.
// If this is 0 then the client does not fall back to less optimal but available methods.
MethodResetDuration time.Duration
// [OPTIONAL] The reth DB path to fetch receipts from.
// If it is specified, the rethdb receipts fetcher will be used
// and the RPC configuration parameters don't need to be set.
RethDBPath string
}
func (c *EthClientConfig) Check() error {
......@@ -82,15 +78,6 @@ func (c *EthClientConfig) Check() error {
if c.PayloadsCacheSize < 0 {
return fmt.Errorf("invalid payloads cache size: %d", c.PayloadsCacheSize)
}
if c.RethDBPath != "" {
if buildRethdb {
// If the rethdb path is set, we use the rethdb receipts fetcher and skip creating
// an RCP receipts fetcher, so below rpc config parameters don't need to be checked.
return nil
} else {
return fmt.Errorf("rethdb path specified, but built without rethdb support")
}
}
if c.MaxConcurrentRequests < 1 {
return fmt.Errorf("expected at least 1 concurrent request, but max is %d", c.MaxConcurrentRequests)
}
......@@ -136,9 +123,9 @@ func NewEthClient(client client.RPC, log log.Logger, metrics caching.Metrics, co
}
client = LimitRPC(client, config.MaxConcurrentRequests)
recProvider := newRecProviderFromConfig(client, log, metrics, config)
recProvider := newRPCRecProviderFromConfig(client, log, metrics, config)
if recProvider.isInnerNil() {
return nil, fmt.Errorf("failed to open RethDB")
return nil, errors.New("failed to establish receipts provider")
}
return &EthClient{
client: client,
......
//go:build rethdb
package sources
import (
"context"
"encoding/json"
"fmt"
"unsafe"
"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"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
/*
#cgo LDFLAGS: -L../rethdb-reader/target/release -lrethdbreader
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct {
char* data;
size_t data_len;
bool error;
} ReceiptsResult;
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);
extern void rdb_free_string(char* string);
extern OpenDBResult open_db_read_only(const char *db_path);
*/
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.
func FetchRethReceipts(db unsafe.Pointer, blockHash *common.Hash) (types.Receipts, error) {
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
receiptsResult := C.rdb_read_receipts((*C.uint8_t)(cBlockHash), C.size_t(len(blockHash)), db)
if receiptsResult.error {
return nil, fmt.Errorf("Error fetching receipts from Reth Database.")
}
// Free the memory allocated by the C code
defer C.rdb_free_string(receiptsResult.data)
// 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
if err := json.Unmarshal([]byte(receiptsJSON), &receipts); err != nil {
return nil, err
}
return receipts, nil
}
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
}
type RethDBReceiptsFetcher struct {
dbInstance unsafe.Pointer
}
var _ ReceiptsProvider = (*RethDBReceiptsFetcher)(nil)
// NewRethDBReceiptsFetcher opens a RethDB for reading receipts. It returns nil if it was unable to open the database
func NewRethDBReceiptsFetcher(dbPath string) *RethDBReceiptsFetcher {
db, err := OpenDBReadOnly(dbPath)
if err != nil {
return nil
}
return &RethDBReceiptsFetcher{
dbInstance: db,
}
}
func (f *RethDBReceiptsFetcher) FetchReceipts(ctx context.Context, block eth.BlockInfo, txHashes []common.Hash) (types.Receipts, error) {
if f.dbInstance == nil {
return nil, fmt.Errorf("Reth dbInstance is nil")
}
hash := block.Hash()
return FetchRethReceipts(f.dbInstance, &hash)
}
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)
}
//go:build !rethdb
package sources
import (
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/sources/caching"
"github.com/ethereum/go-ethereum/log"
)
const buildRethdb = false
func newRecProviderFromConfig(client client.RPC, log log.Logger, metrics caching.Metrics, config *EthClientConfig) *CachingReceiptsProvider {
return newRPCRecProviderFromConfig(client, log, metrics, config)
}
//go:build rethdb
package sources
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
func TestRethDBReceiptsLoad(t *testing.T) {
t.Parallel()
// ETH Mainnet block #18,663,292
//
// https://etherscan.io/tx/0x88b2d153a4e893ba91ac235325c44b1aa0c802fcb42657701e1a73e1c675f7ca
//
// NOTE: The block hash differs from the live block due to a state root mismatch. In order to generate
// a testdata database with only this block in it, the state root of the block was modified.
// Old State Root: 0xaf81a692d228d56d35c80d65aeba59636b4671403054f6c57446c0e3e4d951c8
// New State Root (Empty MPT): 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
blockHash := common.HexToHash("0x6a229123d607c2232a8b0bdd36f90745945d05181018e64e60ff2b93ab6b52e5")
fetcher := NewRethDBReceiptsFetcher("../rethdb-reader/testdata/db")
require.NotNil(t, fetcher.dbInstance)
res, err := FetchRethReceipts(fetcher.dbInstance, &blockHash)
require.NoError(t, err)
receipt := (*types.Receipt)(res[0])
require.Equal(t, receipt.Type, uint8(2))
require.Equal(t, receipt.Status, uint64(1))
require.Equal(t, receipt.CumulativeGasUsed, uint64(115_316))
require.Equal(t, receipt.Bloom, types.BytesToBloom(common.FromHex("00200000000000000000000080001000000000000000000000000000000000000000000000000000000000000000100002000100080000000000000000000000000000000000000000000008000000200000000400000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000400000000000001000000000000000100000000080000004000000000000000000000000000000000000002000000000000000000000000000000000000000006000000000000000000000000000000000000001000000000000000000000200000000000000100000000020000000000000000000000000000000010")))
require.Equal(t, receipt.Logs[0].Address, common.HexToAddress("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"))
require.Equal(t, receipt.Logs[0].Topics[0], common.HexToHash("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"))
require.Equal(t, receipt.Logs[0].Topics[1], common.HexToHash("00000000000000000000000000000000003b3cc22af3ae1eac0440bcee416b40"))
require.Equal(t, receipt.Logs[0].Data, common.FromHex("00000000000000000000000000000000000000000000000008a30cd230000000"))
require.Equal(t, receipt.TxHash, common.HexToHash("0x88b2d153a4e893ba91ac235325c44b1aa0c802fcb42657701e1a73e1c675f7ca"))
require.Equal(t, receipt.BlockHash, blockHash)
require.Equal(t, receipt.BlockNumber, big.NewInt(18_663_292))
require.Equal(t, receipt.TransactionIndex, uint(0))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment