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
2 changes: 2 additions & 0 deletions Cargo.lock

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

153 changes: 149 additions & 4 deletions crates/consensus-logic/src/client_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ pub fn process_event<D: Database>(

writes.push(ClientStateWrite::AcceptL1Block(*l1blkid));

// TODO if we have some number of L1 blocks finalized, also emit an
// `UpdateBuried` write
// If we have some number of L1 blocks finalized, also emit an `UpdateBuried` write
if *height >= params.rollup().l1_reorg_safe_depth as u64 + state.buried_l1_height() {
writes.push(ClientStateWrite::UpdateBuried(state.buried_l1_height() + 1));
}

let l1v = state.l1_view();

Expand All @@ -55,8 +57,13 @@ pub fn process_event<D: Database>(
}
}

SyncEvent::L1Revert(_to_height) => {
// TODO
SyncEvent::L1Revert(to_height) => {
let l1prov = database.l1_provider();
let blkmf = l1prov
.get_block_manifest(*to_height)?
.ok_or(Error::MissingL1BlockHeight(*to_height))?;
let blkid = blkmf.block_hash().into();
writes.push(ClientStateWrite::RollbackL1BlocksTo(blkid));
}

SyncEvent::L1DABatch(blkids) => {
Expand All @@ -79,6 +86,7 @@ pub fn process_event<D: Database>(

let last = blkids.last().unwrap();
writes.push(ClientStateWrite::UpdateFinalized(*last));
actions.push(SyncAction::FinalizeBlock(*last))
} else {
// TODO we can expand this later to make more sense
return Err(Error::MissingClientSyncState);
Expand All @@ -105,3 +113,140 @@ pub fn process_event<D: Database>(

Ok(ClientUpdateOutput::new(writes, actions))
}

#[cfg(test)]
mod tests {
use alpen_express_db::traits::L1DataStore;
use alpen_express_primitives::{block_credential, l1::L1BlockManifest};
use alpen_express_state::{l1::L1BlockId, operation};
use alpen_test_utils::{
bitcoin::gen_l1_chain,
get_common_db,
l2::{gen_client_state, gen_params},
ArbitraryGenerator,
};

use super::*;

#[test]
fn handle_l1_block() {
let database = get_common_db();
let params = gen_params();
let mut state = gen_client_state(Some(&params));

assert!(!state.is_chain_active());
let l1_chain = gen_l1_chain(15);

// before the genesis of L2 is reached
{
let height = 1;
let l1_block_id = l1_chain[height as usize].block_hash().into();
let event = SyncEvent::L1Block(height, l1_block_id);

let output = process_event(&state, &event, database.as_ref(), &params).unwrap();

let writes = output.writes();
let actions = output.actions();

let expection_writes = [ClientStateWrite::AcceptL1Block(l1_block_id)];
let expected_actions = [];

assert_eq!(writes, expection_writes);
assert_eq!(actions, expected_actions);

operation::apply_writes_to_state(&mut state, writes.iter().cloned());

assert!(!state.is_chain_active());
assert_eq!(state.recent_l1_block(), Some(&l1_block_id));
assert_eq!(state.buried_l1_height(), params.rollup.genesis_l1_height);
assert_eq!(state.l1_view().local_unaccepted_blocks(), [l1_block_id]);
}

// after the genesis of L2 is reached
{
let height = params.rollup.genesis_l1_height + 3;
let l1_block_id = l1_chain[height as usize].block_hash().into();
let event = SyncEvent::L1Block(height, l1_block_id);

let output = process_event(&state, &event, database.as_ref(), &params).unwrap();

let expection_writes = [
ClientStateWrite::AcceptL1Block(l1_block_id),
ClientStateWrite::ActivateChain,
];
let expected_actions = [];

assert_eq!(output.writes(), expection_writes);
assert_eq!(output.actions(), expected_actions);

operation::apply_writes_to_state(&mut state, output.writes().iter().cloned());

assert!(state.is_chain_active());
assert_eq!(state.recent_l1_block(), Some(&l1_block_id));
assert_eq!(state.buried_l1_height(), params.rollup.genesis_l1_height);
assert_eq!(
state.l1_view().local_unaccepted_blocks(),
[l1_chain[1].block_hash().into(), l1_block_id,]
);
}

// after l1_reorg_depth is reached
{
let height = params.rollup.genesis_l1_height + params.rollup.l1_reorg_safe_depth as u64;
let l1_block_id = l1_chain[height as usize].block_hash().into();
let event = SyncEvent::L1Block(height, l1_block_id);

let output = process_event(&state, &event, database.as_ref(), &params).unwrap();

let expection_writes = [
ClientStateWrite::AcceptL1Block(l1_block_id),
ClientStateWrite::UpdateBuried(params.rollup.genesis_l1_height + 1),
];
let expected_actions = [];

assert_eq!(output.writes(), expection_writes);
assert_eq!(output.actions(), expected_actions);

operation::apply_writes_to_state(&mut state, output.writes().iter().cloned());

assert!(state.is_chain_active());
assert_eq!(state.recent_l1_block(), Some(&l1_block_id));
assert_eq!(
state.buried_l1_height(),
params.rollup.genesis_l1_height + 1
);
assert_eq!(
state.l1_view().local_unaccepted_blocks(),
[l1_chain[8].block_hash().into(), l1_block_id,]
);
}
}

#[test]
fn handle_l1_revert() {
let database = get_common_db();
let params = gen_params();
let mut state = gen_client_state(Some(&params));

let height = 5;
let event = SyncEvent::L1Revert(height);

let output = process_event(&state, &event, database.as_ref(), &params);
assert!(output.is_err_and(|x| matches!(x, Error::MissingL1BlockHeight(height))));

let l1_block: L1BlockManifest = ArbitraryGenerator::new().generate();
database
.l1_store()
.put_block_data(height, l1_block.clone(), vec![])
.unwrap();

let output = process_event(&state, &event, database.as_ref(), &params).unwrap();
let expectation_writes = [ClientStateWrite::RollbackL1BlocksTo(
l1_block.block_hash().into(),
)];
let expected_actions = [];

assert_eq!(output.actions(), expected_actions);
assert_eq!(output.writes(), expectation_writes);
}
}
1 change: 1 addition & 0 deletions crates/consensus-logic/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ fn handle_sync_event<D: Database, E: ExecEngineCtl>(
// TODO we should probably emit a state checkpoint here if we
// aren't already
info!(?blkid, "finalizing block");
engine.update_finalized_block(*blkid)?;
}

SyncAction::L2Genesis(l1blkid) => {
Expand Down
3 changes: 3 additions & 0 deletions crates/primitives/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct RollupParams {
/// TODO: move elsewhere
pub evm_genesis_block_hash: Buf32,
pub evm_genesis_block_state_root: Buf32,

/// Depth after which we consider the L1 block to not reorg
pub l1_reorg_safe_depth: u32,
}

/// Client sync parameters that are used to make the network work but don't
Expand Down
4 changes: 4 additions & 0 deletions crates/state/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ impl ClientState {
pub fn buried_l1_height(&self) -> u64 {
self.local_l1_view.buried_l1_height
}

pub fn genesis_l1_height(&self) -> u64 {
self.genesis_l1_height
}
}

/// Relates to our view of the L2 chain, does not exist before genesis.
Expand Down
2 changes: 2 additions & 0 deletions crates/test-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ version = "0.1.0"

[dependencies]
alpen-express-db = { workspace = true, features = ["stubs"] }
alpen-express-primitives = { workspace = true }
alpen-express-rocksdb = { workspace = true }
alpen-express-state = { workspace = true }

anyhow = { workspace = true }
arbitrary = { workspace = true }
Expand Down
12 changes: 12 additions & 0 deletions crates/test-utils/src/bitcoin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use alpen_express_primitives::l1::L1BlockManifest;
use bitcoin::{consensus::deserialize, Transaction};

use crate::ArbitraryGenerator;

pub fn get_test_bitcoin_txns() -> Vec<Transaction> {
let t1 = "0200000000010176f29f18c5fc677ad6dd6c9309f6b9112f83cb95889af21da4be7fbfe22d1d220000000000fdffffff0300e1f505000000002200203946555814a18ccc94ef4991fb6af45278425e6a0a2cfc2bf4cf9c47515c56ff0000000000000000176a1500e0e78c8201d91f362c2ad3bb6f8e6f31349454663b1010240100000022512012d77c9ae5fdca5a3ab0b17a29b683fd2690f5ad56f6057a000ec42081ac89dc0247304402205de15fbfb413505a3563608dad6a73eb271b4006a4156eeb62d1eacca5efa10b02201eb71b975304f3cbdc664c6dd1c07b93ac826603309b3258cb92cfd201bb8792012102f55f96fd587a706a7b5e7312c4e9d755a65b3dad9945d65598bca34c9e961db400000000";
let t2 = "02000000000101f4f2e8830d2948b5e980e739e61b23f048d03d4af81588bf5da4618406c495aa0000000000fdffffff02969e0700000000002200203946555814a18ccc94ef4991fb6af45278425e6a0a2cfc2bf4cf9c47515c56ff60f59000000000001600148d0499ec043b1921a608d24690b061196e57c927040047304402203875f7b610f8783d5f5c163118eeec1a23473dd33b53c8ea584c7d28a82b209b022034b6814344b79826a348e23cc19ff06ed2df23850b889557552e376bf9e32c560147304402200f647dad3c137ff98d7da7a302345c82a57116a3d0e6a3719293bbb421cb0abe02201c04a1e808f5bab3595f77985af91aeaf61e9e042c9ac97d696e0f4b020cb54b0169522102dba8352965522ff44538dde37d793b3b4ece54e07759ade5f648aa396165d2962103c0683712773b725e7fe4809cbc90c9e0b890c45e5e24a852a4c472d1b6e9fd482103bf56f172d0631a7f8ae3ef648ad43a816ad01de4137ba89ebc33a2da8c48531553ae00000000";
Expand All @@ -11,3 +14,12 @@ pub fn get_test_bitcoin_txns() -> Vec<Transaction> {
.map(|x| deserialize(&hex::decode(x).unwrap()).unwrap())
.collect()
}

pub fn gen_l1_chain(len: usize) -> Vec<L1BlockManifest> {
let mut blocks = vec![];
for _ in 0..len {
let block: L1BlockManifest = ArbitraryGenerator::new().generate();
blocks.push(block);
}
blocks
}
42 changes: 42 additions & 0 deletions crates/test-utils/src/l2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use alpen_express_primitives::{
block_credential,
buf::Buf32,
params::{Params, RollupParams, RunParams},
};
use alpen_express_state::client_state::ClientState;

pub fn gen_params() -> Params {
Params {
rollup: RollupParams {
block_time: 1000,
cred_rule: block_credential::CredRule::Unchecked,
horizon_l1_height: 3,
genesis_l1_height: 5,
evm_genesis_block_hash: Buf32(
"0x37ad61cff1367467a98cf7c54c4ac99e989f1fbb1bc1e646235e90c065c565ba"
.parse()
.unwrap(),
),
evm_genesis_block_state_root: Buf32(
"0x351714af72d74259f45cd7eab0b04527cd40e74836a45abcae50f92d919d988f"
.parse()
.unwrap(),
),
l1_reorg_safe_depth: 5,
},
run: RunParams {
l1_follow_distance: 3,
},
}
}

pub fn gen_client_state(params: Option<&Params>) -> ClientState {
let params = match params {
Some(p) => p,
None => &gen_params(),
};
ClientState::from_genesis_params(
params.rollup.genesis_l1_height,
params.rollup.genesis_l1_height,
)
}
1 change: 1 addition & 0 deletions crates/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rockbound::rocksdb;
use tempfile::TempDir;

pub mod bitcoin;
pub mod l2;

pub struct ArbitraryGenerator {
buffer: Vec<u8>,
Expand Down
1 change: 1 addition & 0 deletions sequencer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fn main_inner(args: Args) -> anyhow::Result<()> {
.parse()
.unwrap(),
),
l1_reorg_safe_depth: config.sync.max_reorg_depth,
},
run: RunParams {
l1_follow_distance: config.sync.l1_follow_distance,
Expand Down