mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
opt: reduce allocs for parse_deposits_from_receipts (#11949)
This commit is contained in:
@ -1,12 +1,18 @@
|
||||
//! EIP-6110 deposit requests parsing
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use alloy_eips::eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS;
|
||||
use alloy_primitives::{Bytes, Log};
|
||||
use alloy_primitives::{Address, Bytes, Log};
|
||||
use alloy_sol_types::{sol, SolEvent};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_chainspec::{ChainSpec, EthChainSpec};
|
||||
use reth_evm::execute::BlockValidationError;
|
||||
use reth_primitives::Receipt;
|
||||
|
||||
/// The size of a deposit request in bytes. While the event fields emit
|
||||
/// bytestrings, those bytestrings are fixed size. The fields are: 48-byte
|
||||
/// pubkey, 32-byte withdrawal credentials, 8-byte amount, 96-byte signature,
|
||||
/// and 8-byte index.
|
||||
const DEPOSIT_BYTES_SIZE: usize = 48 + 32 + 8 + 96 + 8;
|
||||
|
||||
sol! {
|
||||
#[allow(missing_docs)]
|
||||
event DepositEvent(
|
||||
@ -18,53 +24,79 @@ sol! {
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse [deposit contract](https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa)
|
||||
/// (address is from the passed [`ChainSpec`]) deposits from receipts, and return them as a
|
||||
/// [vector](Vec) of (requests)[`alloy_eips::eip7685::Requests`].
|
||||
/// Accumulate a deposit request from a log. containing a [`DepositEvent`].
|
||||
pub fn accumulate_deposit_from_log(log: &Log<DepositEvent>, out: &mut Vec<u8>) {
|
||||
out.reserve(DEPOSIT_BYTES_SIZE);
|
||||
out.extend_from_slice(log.pubkey.as_ref());
|
||||
out.extend_from_slice(log.withdrawal_credentials.as_ref());
|
||||
out.extend_from_slice(log.amount.as_ref());
|
||||
out.extend_from_slice(log.signature.as_ref());
|
||||
out.extend_from_slice(log.index.as_ref());
|
||||
}
|
||||
|
||||
/// Accumulate deposits from an iterator of logs.
|
||||
pub fn accumulate_deposits_from_logs<'a>(
|
||||
address: Address,
|
||||
logs: impl IntoIterator<Item = &'a Log>,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), BlockValidationError> {
|
||||
logs.into_iter().filter(|log| log.address == address).try_for_each(|log| {
|
||||
// We assume that the log is valid because it was emitted by the
|
||||
// deposit contract.
|
||||
let decoded_log =
|
||||
DepositEvent::decode_log(log, false).map_err(|err: alloy_sol_types::Error| {
|
||||
BlockValidationError::DepositRequestDecode(err.to_string())
|
||||
})?;
|
||||
accumulate_deposit_from_log(&decoded_log, out);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Accumulate deposits from a receipt. Iterates over the logs in the receipt
|
||||
/// and accumulates the deposit request bytestrings.
|
||||
pub fn accumulate_deposits_from_receipt(
|
||||
address: Address,
|
||||
receipt: &Receipt,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), BlockValidationError> {
|
||||
accumulate_deposits_from_logs(address, &receipt.logs, out)
|
||||
}
|
||||
|
||||
/// Accumulate deposits from a list of receipts. Iterates over the logs in the
|
||||
/// receipts and accumulates the deposit request bytestrings.
|
||||
pub fn accumulate_deposits_from_receipts<'a, I>(
|
||||
address: Address,
|
||||
receipts: I,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), BlockValidationError>
|
||||
where
|
||||
I: IntoIterator<Item = &'a Receipt>,
|
||||
{
|
||||
receipts
|
||||
.into_iter()
|
||||
.try_for_each(|receipt| accumulate_deposits_from_receipt(address, receipt, out))
|
||||
}
|
||||
|
||||
/// Find deposit logs in a list of receipts, and return the concatenated
|
||||
/// deposit request bytestring.
|
||||
///
|
||||
/// The address of the deposit contract is taken from the chain spec, and
|
||||
/// defaults to [`MAINNET_DEPOSIT_CONTRACT_ADDRESS`] if not specified in
|
||||
/// the chain spec.
|
||||
pub fn parse_deposits_from_receipts<'a, I>(
|
||||
chain_spec: &ChainSpec,
|
||||
chainspec: &ChainSpec,
|
||||
receipts: I,
|
||||
) -> Result<Bytes, BlockValidationError>
|
||||
where
|
||||
I: IntoIterator<Item = &'a Receipt>,
|
||||
{
|
||||
let mut requests = Vec::new();
|
||||
let deposit_contract_address = chain_spec
|
||||
.deposit_contract
|
||||
.as_ref()
|
||||
.map_or(MAINNET_DEPOSIT_CONTRACT_ADDRESS, |contract| contract.address);
|
||||
let logs: Vec<_> = receipts
|
||||
.into_iter()
|
||||
.flat_map(|receipt| &receipt.logs)
|
||||
// No need to filter for topic because there's only one event and that's the Deposit
|
||||
// event in the deposit contract.
|
||||
.filter(|log| log.address == deposit_contract_address)
|
||||
.collect();
|
||||
|
||||
for log in &logs {
|
||||
let decoded_log =
|
||||
DepositEvent::decode_log(log, false).map_err(|err: alloy_sol_types::Error| {
|
||||
BlockValidationError::DepositRequestDecode(err.to_string())
|
||||
})?;
|
||||
requests.extend(parse_deposit_from_log(&decoded_log).as_ref())
|
||||
}
|
||||
|
||||
Ok(requests.into())
|
||||
}
|
||||
|
||||
fn parse_deposit_from_log(log: &Log<DepositEvent>) -> Bytes {
|
||||
// SAFETY: These `expect` https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/solidity_deposit_contract/deposit_contract.sol#L107-L110
|
||||
// are safe because the `DepositEvent` is the only event in the deposit contract and the length
|
||||
// checks are done there.
|
||||
[
|
||||
log.pubkey.as_ref(),
|
||||
log.withdrawal_credentials.as_ref(),
|
||||
log.amount.as_ref(),
|
||||
log.signature.as_ref(),
|
||||
log.index.as_ref(),
|
||||
]
|
||||
.concat()
|
||||
.into()
|
||||
let mut out = Vec::new();
|
||||
accumulate_deposits_from_receipts(
|
||||
chainspec.deposit_contract().map(|c| c.address).unwrap_or(MAINNET_DEPOSIT_CONTRACT_ADDRESS),
|
||||
receipts,
|
||||
&mut out,
|
||||
)?;
|
||||
Ok(out.into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user