Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
7d603267
Commit
7d603267
authored
Oct 26, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test: hydrated receipts
parent
0d752667
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
152 additions
and
85 deletions
+152
-85
Cargo.lock
op-service/rethdb-reader/Cargo.lock
+2
-0
Cargo.toml
op-service/rethdb-reader/Cargo.toml
+2
-0
lib.rs
op-service/rethdb-reader/src/lib.rs
+134
-59
reth_db.go
op-service/sources/reth_db.go
+14
-26
No files found.
op-service/rethdb-reader/Cargo.lock
View file @
7d603267
...
...
@@ -5361,6 +5361,8 @@ name = "rethdb-reader"
version = "0.1.0"
dependencies = [
"reth",
"serde",
"serde_json",
]
[[package]]
...
...
op-service/rethdb-reader/Cargo.toml
View file @
7d603267
...
...
@@ -10,3 +10,5 @@ crate-type = ["cdylib"]
[dependencies]
reth
=
{
git
=
"https://github.com/paradigmxyz/reth.git"
}
serde
=
"1.0.190"
serde_json
=
"1.0.107"
op-service/rethdb-reader/src/lib.rs
View file @
7d603267
use
reth
::{
blockchain_tree
::
noop
::
NoopBlockchainTree
,
primitives
::{
alloy_primitives
::
private
::
alloy_rlp
::
Encodable
,
BlockHashOrNumber
,
ChainSpecBuilder
,
BlockHashOrNumber
,
ChainSpecBuilder
,
Receipt
,
TransactionMeta
,
TransactionSigned
,
U128
,
U256
,
U64
,
},
providers
::{
providers
::
BlockchainProvider
,
ProviderFactory
,
ReceiptProvider
},
providers
::{
providers
::
BlockchainProvider
,
BlockReader
,
ProviderFactory
,
ReceiptProvider
},
revm
::
primitives
::
calc_blob_gasprice
,
rpc
::
types
::{
Log
,
TransactionReceipt
},
utils
::
db
::
open_db_read_only
,
};
use
std
::{
os
::
raw
::
c_char
,
path
::
Path
,
sync
::
Arc
};
#[repr(C)]
pub
struct
ByteArray
{
data
:
*
mut
u8
,
len
:
usize
,
}
#[repr(C)]
pub
struct
ByteArrays
{
data
:
*
mut
ByteArray
,
len
:
usize
,
}
#[repr(C)]
pub
struct
ReceiptsResult
{
receipts
:
ByteArrays
,
data
:
*
mut
char
,
data_len
:
usize
,
error
:
bool
,
}
// Implement a default for ByteArrays to be used in error cases
impl
Default
for
ByteArrays
{
fn
default
()
->
Self
{
ByteArrays
{
data
:
std
::
ptr
::
null_mut
(),
len
:
0
,
}
}
}
impl
ReceiptsResult
{
pub
fn
success
(
receipts
:
ByteArrays
)
->
Self
{
pub
fn
success
(
data
:
*
mut
char
,
data_len
:
usize
)
->
Self
{
Self
{
receipts
,
data
,
data_len
,
error
:
false
,
}
}
pub
fn
fail
()
->
Self
{
Self
{
receipts
:
ByteArrays
::
default
(),
data
:
std
::
ptr
::
null_mut
(),
data_len
:
0
,
error
:
true
,
}
}
...
...
@@ -89,56 +73,147 @@ pub extern "C" fn read_receipts(
return
ReceiptsResult
::
fail
();
};
let
Ok
(
block
)
=
provider
.block_by_hash
(
block_hash
.into
())
else
{
return
ReceiptsResult
::
fail
();
};
let
Ok
(
receipts
)
=
provider
.receipts_by_block
(
BlockHashOrNumber
::
Hash
(
block_hash
.into
()))
else
{
return
ReceiptsResult
::
fail
();
};
if
let
Some
(
receipts
)
=
receipts
{
let
receipts_rlp
=
receipts
if
let
(
Some
(
block
),
Some
(
receipts
))
=
(
block
,
receipts
)
{
let
block_number
=
block
.number
;
let
base_fee
=
block
.base_fee_per_gas
;
let
block_hash
=
block
.hash_slow
();
let
excess_blob_gas
=
block
.excess_blob_gas
;
let
Some
(
receipts
)
=
block
.body
.into_iter
()
.
map
(|
r
|
{
// todo - reduce alloc?
// RLP encode the receipt with a bloom filter.
let
m
ut
buf
=
Vec
::
default
();
r
.with_bloom
()
.encode
(
&
mut
buf
);
// Return a pointer to the `buf` and its length
let
res
=
ByteArray
{
data
:
buf
.as_mut_ptr
()
,
len
:
buf
.len
()
,
.
zip
(
receipts
.clone
())
.enumerate
()
.map
(|(
idx
,
(
tx
,
receipt
))|
{
let
m
eta
=
TransactionMeta
{
tx_hash
:
tx
.hash
,
index
:
idx
as
u64
,
block_hash
,
block_number
,
base_fee
,
excess_blob_gas
,
};
// Forget the `buf` so that its memory isn't freed by the
// borrow checker at the end of this scope
std
::
mem
::
forget
(
buf
);
res
build_transaction_receipt_with_block_receipts
(
tx
,
meta
,
receipt
,
&
receipts
)
})
.collect
::
<
Vec
<
_
>>
();
.collect
::
<
Option
<
Vec
<
_
>>>
()
else
{
return
ReceiptsResult
::
fail
();
};
let
result
=
ByteArrays
{
data
:
receipts_rlp
.as_ptr
()
as
*
mut
ByteArray
,
len
:
receipts_rlp
.len
(),
// Convert the receipts to JSON for transport
let
Ok
(
mut
receipts_json
)
=
serde_json
::
to_string
(
&
receipts
)
else
{
return
ReceiptsResult
::
fail
();
};
// Forget the `receipts_rlp` arr so that its memory isn't freed by the
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_
rlp
);
// Prevent Rust from freeing the memory
std
::
mem
::
forget
(
receipts_
json
);
// Prevent Rust from freeing the memory
ReceiptsResult
::
success
(
result
)
res
}
else
{
return
ReceiptsResult
::
fail
();
ReceiptsResult
::
fail
()
}
}
/// Free
the [ByteArrays] data structure and its sub-components when they are no longer needed
.
/// Free
a string that was allocated in Rust and passed to C
.
#[no_mangle]
pub
extern
"C"
fn
free_
byte_arrays
(
arrays
:
ByteArrays
)
{
pub
extern
"C"
fn
free_
string
(
string
:
*
mut
c_char
)
{
unsafe
{
let
arrays
=
Vec
::
from_raw_parts
(
arrays
.data
,
arrays
.len
,
arrays
.len
);
for
inner_array
in
arrays
{
let
_
=
Vec
::
from_raw_parts
(
inner_array
.data
,
inner_array
.len
,
inner_array
.len
);
// 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
);
}
}
}
pub
(
crate
)
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
:
meta
.excess_blob_gas
.map
(
calc_blob_gasprice
)
.map
(
U128
::
from
),
blob_gas_used
:
transaction
.transaction
.blob_gas_used
()
.map
(
U128
::
from
),
};
match
tx
.transaction
.kind
()
{
reth
::
primitives
::
TransactionKind
::
Create
=>
{
res_receipt
.contract_address
=
Some
(
transaction
.signer
()
.create
(
tx
.transaction
.nonce
()));
}
reth
::
primitives
::
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
)
}
op-service/sources/reth_db.go
View file @
7d603267
package
sources
import
(
"encoding/json"
"fmt"
"unsafe"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
...
...
@@ -14,26 +17,15 @@ import (
#include <stdbool.h>
typedef struct {
uint8_t* data;
size_t len;
} ByteArray;
typedef struct {
ByteArray* data;
size_t len;
} ByteArrays;
// Define ReceiptsResult with a bool for error
typedef struct {
ByteArrays receipts;
char* data;
size_t data_len;
bool error;
} ReceiptsResult;
extern ReceiptsResult read_receipts(const uint8_t* block_hash, size_t block_hash_len, const char* db_path);
extern void free_
byte_arrays(ByteArrays arrays
);
extern void free_
string(char* string
);
*/
import
"C"
import
"unsafe"
// 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.
...
...
@@ -51,23 +43,19 @@ func FetchRethReceipts(dbPath string, blockHash *common.Hash) (types.Receipts, e
defer
C
.
free
(
unsafe
.
Pointer
(
cDbPath
))
// Call the C function to fetch the receipts from the Reth Database
byteArrayStruc
t
:=
C
.
read_receipts
((
*
C
.
uint8_t
)(
cBlockHash
),
C
.
size_t
(
len
(
blockHash
)),
cDbPath
)
receiptsResul
t
:=
C
.
read_receipts
((
*
C
.
uint8_t
)(
cBlockHash
),
C
.
size_t
(
len
(
blockHash
)),
cDbPath
)
if
byteArrayStruc
t
.
error
{
if
receiptsResul
t
.
error
{
return
nil
,
fmt
.
Errorf
(
"Error fetching receipts from Reth Database."
)
}
// Convert the returned receipt RLP byte arrays to decoded Receipts.
data
:=
make
(
types
.
Receipts
,
byteArrayStruct
.
receipts
.
len
)
byteArraySlice
:=
(
*
[
1
<<
30
]
C
.
ByteArray
)(
unsafe
.
Pointer
(
byteArrayStruct
.
receipts
.
data
))[
:
byteArrayStruct
.
receipts
.
len
:
byteArrayStruct
.
receipts
.
len
]
for
i
,
byteArray
:=
range
byteArraySlice
{
receipt
:=
types
.
Receipt
{}
receipt
.
UnmarshalBinary
(
C
.
GoBytes
(
unsafe
.
Pointer
(
byteArray
.
data
),
C
.
int
(
byteArray
.
len
)))
data
[
i
]
=
&
receipt
}
// 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
json
.
Unmarshal
([]
byte
(
receiptsJSON
),
&
receipts
)
// Free the memory allocated by the C code
C
.
free_
byte_arrays
(
byteArrayStruct
.
receipts
)
C
.
free_
string
(
receiptsResult
.
data
)
return
data
,
nil
return
receipts
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment