Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GWYNETH] reorg support #48

Draft
wants to merge 3 commits into
base: gwyneth
Choose a base branch
from
Draft
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
134 changes: 126 additions & 8 deletions crates/gwyneth/src/exex.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{marker::PhantomData, sync::Arc};
use std::{marker::PhantomData, sync::Arc, collections::VecDeque};

use alloy_rlp::Decodable;
use alloy_sol_types::{sol, SolEventInterface};
use reth_revm::database::EvmStateProvider;

use crate::{
engine_api::EngineApiContext, GwynethEngineTypes, GwynethNode, GwynethPayloadAttributes,
Expand Down Expand Up @@ -33,6 +34,15 @@
const ROLLUP_CONTRACT_ADDRESS: Address = address!("9fCF7D13d10dEdF17d0f24C62f0cf4ED462f65b7");
pub const CHAIN_ID: u64 = 167010;
const INITIAL_TIMESTAMP: u64 = 1710338135;
const RING_BUFFER_SIZE: usize = 128;


#[derive(Clone, Debug)]
struct L1L2Mapping {
l1_block: u64,
l2_block: u64,
l2_hash: B256,
}

pub type GwynethFullNode = FullNode<
NodeAdapter<
Expand Down Expand Up @@ -71,6 +81,7 @@
ctx: ExExContext<Node>,
node: GwynethFullNode,
engine_api: EngineApiContext<GwynethEngineTypes>,
l1_l2_ring_buffer: VecDeque<L1L2Mapping>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe can also just use an array/vec of constant RING_BUFFER_SIZE size, and then index into this array as l1_block_number % RING_BUFFER_SIZE. Might be easier, don't know.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use VecDeque rather, cause it works well for ring buffers. Using pop_front() & push_back() , so the placement of blocks for caching is contagious.
With %, you need to deal with edge cases. (E.g.: you are in the "bucket" of 127, and you need to reorg. But in the last 127 blocks there were no L2 block, so then you need to go till the 128, so basically traverse backwards and found the proper L2 info. With VecDequeue you just need to go backwards till the L1_saved < L1 reorged).

}

impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
Expand All @@ -80,14 +91,20 @@
canonical_stream: node.provider.canonical_state_stream(),
_marker: PhantomData::<GwynethEngineTypes>,
};
Ok(Self { ctx, node, /* payload_event_stream, */ engine_api })
Ok(Self {
ctx,
node,
/* payload_event_stream, */
engine_api,
l1_l2_ring_buffer: VecDeque::with_capacity(RING_BUFFER_SIZE),
})
}

pub async fn start(mut self) -> eyre::Result<()> {
// Process all new chain state notifications
while let Some(notification) = self.ctx.notifications.recv().await {
if let Some(reverted_chain) = notification.reverted_chain() {
self.revert(&reverted_chain)?;
self.revert(&reverted_chain).await?;
}

if let Some(committed_chain) = notification.committed_chain() {
Expand All @@ -106,21 +123,21 @@
pub async fn commit(&mut self, chain: &Chain) -> eyre::Result<()> {
let events = decode_chain_into_rollup_events(chain);
for (block, _, event) in events {
// TODO: Don't emit ProposeBlock event but directely

Check failure on line 126 in crates/gwyneth/src/exex.rs

View workflow job for this annotation

GitHub Actions / codespell

directely ==> directly
// read the function call RollupContractCalls to extract Txs
// let _call = RollupContractCalls::abi_decode(tx.input(), true)?;

if let RollupContractEvents::BlockProposed(BlockProposed {
blockId: block_number,
blockId: l2_block_number,
meta,
}) = event
{
println!("block_number: {:?}", block_number);
println!("block_number: {:?}", l2_block_number);
println!("tx_list: {:?}", meta.txList);
let transactions: Vec<TransactionSigned> = decode_transactions(&meta.txList);
println!("transactions: {:?}", transactions);

let attrs = GwynethPayloadAttributes {
let attrs: GwynethPayloadAttributes = GwynethPayloadAttributes {
inner: EthPayloadAttributes {
timestamp: INITIAL_TIMESTAMP,
prev_randao: B256::ZERO,
Expand All @@ -140,6 +157,8 @@
.state_provider_by_block_number(block.number)
.unwrap();

let l1_block_number = block.number;

let mut builder_attrs =
GwynethPayloadBuilderAttributes::try_new(B256::ZERO, attrs).unwrap();
builder_attrs.l1_provider =
Expand Down Expand Up @@ -175,16 +194,115 @@
)
.await?;

// Convert l2_block_number to u64 if necessary
let l2_block_u64 = l2_block_number.try_into().unwrap_or(u64::MAX);

// Update the L1-L2 mapping in the ring buffer
self.update_l1_l2_ring_buffer(l1_block_number, l2_block_u64, block_hash);

// trigger forkchoice update via engine api to commit the block to the blockchain
self.engine_api.update_forkchoice(block_hash, block_hash).await?;

// TEST CODE!!
println!("Dani:about to revert, but checking get l2_block_u64: {}", l2_block_u64);
if (l2_block_u64 == 3u64) {
//Check reverting back to state 1, which shall be proposed before block nr 40.. and we will go back to that.
self.revert_test(40).await?;
}
}
}

Ok(())
}

fn revert(&mut self, chain: &Chain) -> eyre::Result<()> {
unimplemented!()
pub async fn revert(&mut self, chain: &Chain) -> eyre::Result<()> {
// Find the oldest L1 block number (and subtract 1) in the given chain
let oldest_l1_block = chain.blocks().keys().min().copied()
.ok_or_else(|| eyre::eyre!("Chain is empty"))?
.saturating_sub(1);

// Find the corresponding or closest prior L2 block
let l2_block_hash = self.find_l2_block_hash(oldest_l1_block);

// Update forkchoice
if let Some(block_hash) = l2_block_hash {
self.engine_api.update_forkchoice(block_hash, block_hash).await?;
// Remove all mappings newer than the reverted block
self.l1_l2_ring_buffer.retain(|mapping| mapping.l1_block <= oldest_l1_block);
}

// Remove all mappings newer than the reverted block
self.l1_l2_ring_buffer.retain(|mapping| mapping.l1_block <= oldest_l1_block);

Ok(())
}

pub async fn revert_test(&mut self, oldest_l1_block: u64) -> eyre::Result<()> {
// Simulate to: find the oldest L1 block number (and subtract 1) in the given chain
let revertTo = oldest_l1_block-1;

// Find the corresponding or closest prior L2 block
let l2_block_hash = self.find_l2_block_hash(oldest_l1_block);

println!("Dani: l1_block we need a snapshot from {}", oldest_l1_block);
println!("Dani: l2_blockhash we found {:?}", l2_block_hash);

// Update forkchoice
if let Some(block_hash) = l2_block_hash {

println!("Dani: reverting to: {}", block_hash);
self.engine_api.update_forkchoice(block_hash, block_hash).await?;

println!("Dani: reverted");
// Remove all mappings newer than the reverted block
self.l1_l2_ring_buffer.retain(|mapping| mapping.l1_block <= oldest_l1_block);
}

Ok(())
}

fn find_l2_block_hash(&self, l1_block: u64) -> Option<B256> {
// Find the exact match or the closest prior L2 block
self.l1_l2_ring_buffer
.iter()
.rev()
.find(|mapping| mapping.l1_block <= l1_block)
.map(|mapping| mapping.l2_hash)
}

fn update_l1_l2_ring_buffer(&mut self, l1_block: u64, l2_block: u64, l2_hash: B256) {
// Check if we already have an L2 enthy for this L1 block
if let Some(existing_index) = self.l1_l2_ring_buffer.iter().position(|m| m.l1_block == l1_block) {
// We have an existing entry, check if the new L2 block is higher
let existing_mapping = &mut self.l1_l2_ring_buffer[existing_index];
if l2_block > existing_mapping.l2_block {
existing_mapping.l2_block = l2_block;
existing_mapping.l2_hash = l2_hash;
}
} else {
// No existing entry for this L1 block, add a new one
let mapping = L1L2Mapping {
l1_block,
l2_block,
l2_hash,
};

if self.l1_l2_ring_buffer.len() == RING_BUFFER_SIZE {
// If the buffer is full, remove the oldest entry
self.l1_l2_ring_buffer.pop_front();
}

// Add the new mapping to the end of the buffer
self.l1_l2_ring_buffer.push_back(mapping);
}
}

// New method to get the L2 block info for a given L1 block number
pub fn get_l2_info_for_l1_block(&self, l1_block: u64) -> Option<(u64, B256)> {
self.l1_l2_ring_buffer
.iter()
.find(|mapping| mapping.l1_block == l1_block)
.map(|mapping| (mapping.l2_block, mapping.l2_hash))
}
}

Expand Down
Loading