Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 11 additions & 19 deletions shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ use alloy::{
consensus::Eip658Value,
hex,
network::EthereumWallet,
primitives::{Address, Bloom, Log, B256},
primitives::{Address, Bloom, Bytes, FixedBytes, Log, B256, U256},
providers::{
fillers::{
BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller,
WalletFiller,
},
Identity, ProviderBuilder, RootProvider,
},
rpc::types::Block,
signers::local::PrivateKeySigner,
};
use async_trait::async_trait;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::HashMap,
fmt::{Display, Formatter},
str::FromStr as _,
};
Expand All @@ -28,11 +28,14 @@ pub trait GetBlockRef {
fn block_ref(&self) -> &BlockRef;
}

/// auxiliary data for delayed messages in a hashmap by sequence number to msg contents
pub type DelayedMsgsData = HashMap<U256, Bytes>;

/// A trait for building blocks from the sequencing and settlement chains.
#[async_trait]
pub trait BlockBuilder<T>: Send {
/// Process a single slot
fn build_block(&self, block: &PartialBlock) -> eyre::Result<T>;
fn build_block(&self, block: &PartialBlock, msgs_data: DelayedMsgsData) -> eyre::Result<T>;
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
Expand Down Expand Up @@ -86,27 +89,16 @@ pub struct Receipt {
}

/// `PartialBlock` contains block transactions, event logs, and metadata
#[allow(missing_docs)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct PartialBlock {
/// reference to the block that this partial block was built from
pub block_ref: BlockRef,
/// hash of the parent block
pub parent_hash: B256,
/// log data
pub logs: Vec<Log>,
}

/// Convert a block and its receipts to a `PartialBlock`
pub fn convert_block_to_partial_block(block: &Block, receipts: &[Receipt]) -> PartialBlock {
let filtered_logs: Vec<Log> =
receipts.iter().flat_map(|receipt| receipt.logs.clone()).collect();
PartialBlock {
block_ref: BlockRef {
number: block.header.number,
hash: block.header.hash,
timestamp: block.header.timestamp,
},
parent_hash: block.header.parent_hash,
logs: filtered_logs,
}
/// associated transaction hashes for each log
pub log_tx_hashes: Vec<FixedBytes<32>>,
}

impl GetBlockRef for PartialBlock {
Expand Down
92 changes: 61 additions & 31 deletions shared/test-utils/src/nitro_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{chain_info::PRIVATE_KEY, docker::E2EProcess};
use alloy::{
consensus::{EthereumTxEnvelope, TxEip4844Variant},
network::TransactionBuilder,
primitives::{address, Address, Bytes, B256, U256},
primitives::{address, Address, Bytes, B256, U160, U256},
providers::{Provider, WalletProvider},
};
use contract_bindings::synd::{
Expand Down Expand Up @@ -126,6 +126,19 @@ pub struct NitroBlock {
pub timestamp: U256,
}

const L1_TO_L2_ALIAS_OFFSET: Address = address!("0x1111000000000000000000000000000000001111");
/// Computes the L2 alias of an L1 address.
///
/// When a contract on L1 sends a message to L2 via the Inbox, the sender address
/// on L2 is aliased by adding this offset. This prevents address collisions and
/// distinguishes L1-originated messages from native L2 messages.
pub fn apply_l1_to_l2_alias(l1_address: Address) -> Address {
Address::from(
U160::from_be_slice(&l1_address[..])
.wrapping_add(U160::from_be_slice(&L1_TO_L2_ALIAS_OFFSET[..])),
)
}

pub async fn init_withdrawal_tx(
to_address: Address,
withdrawal_value: U256,
Expand All @@ -147,51 +160,68 @@ pub async fn init_withdrawal_tx(
Ok(tx)
}

pub async fn execute_withdrawal(
to_address: Address,
withdrawal_value: U256,
bridge_address: Address,
settlement_provider: &FilledProvider,
appchain_provider: &FilledProvider,
) -> eyre::Result<()> {
pub struct ExecuteWithdrawalParams<'a> {
pub to_address: Address,
pub withdrawal_value: U256,
pub appchain_block_hash_to_prove: B256,
pub bridge_address: Address,
pub settlement_provider: &'a FilledProvider,
pub appchain_provider: &'a FilledProvider,
pub l2_sender: Address,
pub send_root_size: u64,
pub withdrawal_position: u64,
}

pub async fn execute_withdrawal(params: ExecuteWithdrawalParams<'_>) {
// Generate proof
let node_interface = NodeInterface::new(NODE_INTERFACE_PRECOMPILE_ADDRESS, &appchain_provider);
let proof = node_interface.constructOutboxProof(1, 0).call().await?;
let node_interface =
NodeInterface::new(NODE_INTERFACE_PRECOMPILE_ADDRESS, &params.appchain_provider);
let proof = node_interface
.constructOutboxProof(params.send_root_size, params.withdrawal_position)
.call()
.await
.unwrap();

// Execute withdrawal
let bridge = IBridge::new(bridge_address, &settlement_provider);
let bridge = IBridge::new(params.bridge_address, &params.settlement_provider);
let outbox = IOutbox::new(
IRollupCore::new(bridge.rollup().call().await?, &settlement_provider)
IRollupCore::new(bridge.rollup().call().await.unwrap(), &params.settlement_provider)
.outbox()
.call()
.await?,
&settlement_provider,
.await
.unwrap(),
&params.settlement_provider,
);

let block: NitroBlock =
appchain_provider.raw_request("eth_getBlockByNumber".into(), ("latest", false)).await?;
let block: NitroBlock = params
.appchain_provider
.raw_request("eth_getBlockByHash".into(), (params.appchain_block_hash_to_prove, false))
.await
.unwrap();

let _ = outbox
.executeTransaction(
proof.proof, // proof
U256::from(0), // index
settlement_provider.default_signer_address(), // l2Sender
to_address, // to
block.number, // l2Block,
block.l1_block_number, // l1Block,
block.timestamp, // l2Timestamp,
withdrawal_value, // value
Bytes::new(), // data (always empty)
proof.proof, // proof
U256::from(params.withdrawal_position), // index
params.l2_sender, // l2Sender
params.to_address, // to
block.number, // l2Block,
block.l1_block_number, // l1Block,
block.timestamp, // l2Timestamp,
params.withdrawal_value, // value
Bytes::new(), // data (always empty)
)
// NOTE: manually setting the nonce shouldn't be necessary, likey an artifact of: https://github.com/alloy-rs/alloy/issues/2668
// NOTE: manually setting the nonce shouldn't be necessary, likely an artifact of: https://github.com/alloy-rs/alloy/issues/2668
.nonce(
settlement_provider
.get_transaction_count(settlement_provider.default_signer_address())
.await?,
params
.settlement_provider
.get_transaction_count(params.settlement_provider.default_signer_address())
.await
.unwrap(),
)
.send()
.await?;
Ok(())
.await
.unwrap();
}

#[allow(dead_code)]
Expand Down
1 change: 1 addition & 0 deletions synd-chain-ingestor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version.workspace = true
[dependencies]
alloy = { workspace = true, features = ["provider-ws"] }
clap = { workspace = true, features = ["derive", "env"] }
contract-bindings = { workspace = true }
derivative = { workspace = true }
eyre = { workspace = true }
fs2 = { workspace = true }
Expand Down
Loading
Loading