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.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ signet-bundle = { version = "0.10", path = "crates/bundle" }
signet-constants = { version = "0.10", path = "crates/constants" }
signet-evm = { version = "0.10", path = "crates/evm" }
signet-extract = { version = "0.10", path = "crates/extract" }
signet-journal = { version = "0.10", path = "crates/journal" }
signet-node = { version = "0.10", path = "crates/node" }
signet-sim = { version = "0.10", path = "crates/sim" }
signet-types = { version = "0.10", path = "crates/types" }
signet-tx-cache = { version = "0.10", path = "crates/tx-cache" }
signet-zenith = { version = "0.10", path = "crates/zenith" }

signet-test-utils = { version = "0.10", path = "crates/test-utils" }

# ajj
Expand Down
16 changes: 16 additions & 0 deletions crates/journal/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "signet-journal"
description = "Utilities for working with trevm journals in the Signet chain."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
alloy.workspace = true
futures-util = "0.3.31"
thiserror.workspace = true
trevm.workspace = true
178 changes: 178 additions & 0 deletions crates/journal/src/host.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use crate::JournalMeta;
use alloy::{
consensus::Header,
primitives::{keccak256, Bytes, B256},
};
use std::sync::OnceLock;
use trevm::journal::{BundleStateIndex, JournalDecode, JournalDecodeError, JournalEncode};

/// Journal associated with a host block. The journal is an index over the EVM
/// state changes. It can be used to repopulate
#[derive(Debug, Clone)]
pub struct HostJournal<'a> {
/// The metadata
meta: JournalMeta,

/// The changes.
journal: BundleStateIndex<'a>,

/// The serialized journal
serialized: OnceLock<Bytes>,

/// The hash of the serialized journal
hash: OnceLock<B256>,
}

impl PartialEq for HostJournal<'_> {
fn eq(&self, other: &Self) -> bool {
self.meta == other.meta && self.journal == other.journal
}
}

impl Eq for HostJournal<'_> {}

impl<'a> HostJournal<'a> {
/// Create a new journal.
pub const fn new(meta: JournalMeta, journal: BundleStateIndex<'a>) -> Self {
Self { meta, journal, serialized: OnceLock::new(), hash: OnceLock::new() }
}

/// Deconstruct the `HostJournal` into its parts.
pub fn into_parts(self) -> (JournalMeta, BundleStateIndex<'a>) {
(self.meta, self.journal)
}

/// Get the journal meta.
pub const fn meta(&self) -> &JournalMeta {
&self.meta
}

/// Get the journal.
pub const fn journal(&self) -> &BundleStateIndex<'a> {
&self.journal
}

/// Get the host height.
pub const fn host_height(&self) -> u64 {
self.meta.host_height()
}

/// Get the previous journal hash.
pub const fn prev_journal_hash(&self) -> B256 {
self.meta.prev_journal_hash()
}

/// Get the rollup block header.
pub const fn header(&self) -> &Header {
self.meta.header()
}

/// Get the rollup height.
pub const fn rollup_height(&self) -> u64 {
self.meta.rollup_height()
}

/// Serialize the journal.
pub fn serialized(&self) -> &Bytes {
self.serialized.get_or_init(|| JournalEncode::encoded(self))
}

/// Serialize and hash the journal.
pub fn journal_hash(&self) -> B256 {
*self.hash.get_or_init(|| keccak256(self.serialized()))
}
}

impl JournalEncode for HostJournal<'_> {
fn serialized_size(&self) -> usize {
8 + 32 + self.journal.serialized_size()
}

fn encode(&self, buf: &mut dyn alloy::rlp::BufMut) {
self.meta.encode(buf);
self.journal.encode(buf);
}
}

impl JournalDecode for HostJournal<'static> {
fn decode(buf: &mut &[u8]) -> Result<Self, JournalDecodeError> {
let original = *buf;

let meta = JournalMeta::decode(buf)?;
let journal = JournalDecode::decode(buf)?;

let bytes_read = original.len() - buf.len();
let original = &original[..bytes_read];

Ok(Self {
meta,
journal,
serialized: OnceLock::from(Bytes::copy_from_slice(original)),
hash: OnceLock::from(keccak256(original)),
})
}
}

#[cfg(test)]
pub(crate) mod test {
use super::*;
use alloy::primitives::{Address, KECCAK256_EMPTY, U256};
use std::{borrow::Cow, collections::BTreeMap};
use trevm::{
journal::{AcctDiff, InfoOutcome},
revm::{
database::states::StorageSlot,
state::{AccountInfo, Bytecode},
},
};

pub(crate) fn make_state_diff() -> BundleStateIndex<'static> {
let mut bsi = BundleStateIndex::default();

let bytecode = Bytecode::new_legacy(Bytes::from_static(b"world"));
let code_hash = bytecode.hash_slow();

bsi.new_contracts.insert(code_hash, Cow::Owned(bytecode));

bsi.state.insert(
Address::repeat_byte(0x99),
AcctDiff {
outcome: InfoOutcome::Diff {
old: Cow::Owned(AccountInfo {
balance: U256::from(38),
nonce: 7,
code_hash: KECCAK256_EMPTY,
code: None,
}),
new: Cow::Owned(AccountInfo {
balance: U256::from(23828839),
nonce: 83,
code_hash,
code: None,
}),
},
storage_diff: BTreeMap::from_iter([(
U256::MAX,
Cow::Owned(StorageSlot {
previous_or_original_value: U256::from(123456),
present_value: U256::from(654321),
}),
)]),
},
);
bsi
}

#[test]
fn roundtrip() {
let original = HostJournal::new(
JournalMeta::new(u64::MAX, B256::repeat_byte(0xff), Header::default()),
make_state_diff(),
);

let buf = original.encoded();

let decoded = HostJournal::decode(&mut &buf[..]).unwrap();
assert_eq!(original, decoded);
}
}
35 changes: 35 additions & 0 deletions crates/journal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Signet journal utilities.
//!
//! In general, it is recommended to use the [`Journal`] enum, for forwards
//! compatibility.

#![warn(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
unreachable_pub,
clippy::missing_const_for_fn,
rustdoc::all
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

mod host;
pub use host::HostJournal;

mod meta;
pub use meta::JournalMeta;

mod set;
pub use set::JournalSet;

mod versions;
pub use versions::Journal;

use futures_util::Stream;

/// Any [`Stream`] that produces [`Journal`]s.
pub trait JournalStream<'a>: Stream<Item = Journal<'a>> {}

impl<'a, S> JournalStream<'a> for S where S: Stream<Item = Journal<'a>> {}
89 changes: 89 additions & 0 deletions crates/journal/src/meta.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use alloy::{consensus::Header, primitives::B256};
use trevm::journal::{JournalDecode, JournalDecodeError, JournalEncode};

/// Metadata for a block journal. This includes the block header, the host
/// height, and the hash of the previous journal.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JournalMeta {
/// The host height.
host_height: u64,

/// The hash of the previous journal.
prev_journal_hash: B256,

/// The rollup block header.
header: Header,
}

impl JournalMeta {
/// Create a new `JournalMeta`.
pub const fn new(host_height: u64, prev_journal_hash: B256, header: Header) -> Self {
Self { host_height, prev_journal_hash, header }
}

/// Deconstruct the `JournalMeta` into its parts.
pub fn into_parts(self) -> (u64, B256, Header) {
(self.host_height, self.prev_journal_hash, self.header)
}

/// Get the host height.
pub const fn host_height(&self) -> u64 {
self.host_height
}

/// Get the previous journal hash.
pub const fn prev_journal_hash(&self) -> B256 {
self.prev_journal_hash
}

/// Get the rollup block header.
pub const fn header(&self) -> &Header {
&self.header
}

/// Get the rollup height.
pub const fn rollup_height(&self) -> u64 {
self.header.number
}
}

impl JournalEncode for JournalMeta {
fn serialized_size(&self) -> usize {
8 + 32 + self.header.serialized_size()
}

fn encode(&self, buf: &mut dyn alloy::rlp::BufMut) {
self.host_height.encode(buf);
self.prev_journal_hash.encode(buf);
self.header.encode(buf);
}
}

impl JournalDecode for JournalMeta {
fn decode(buf: &mut &[u8]) -> Result<Self, JournalDecodeError> {
let host_height = JournalDecode::decode(buf)?;
let prev_journal_hash = JournalDecode::decode(buf)?;
let header = JournalDecode::decode(buf)?;

Ok(Self { host_height, prev_journal_hash, header })
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn roundtrip() {
let original = JournalMeta {
host_height: 13871,
prev_journal_hash: B256::repeat_byte(0x7),
header: Header::default(),
};

let buf = original.encoded();

let decoded = JournalMeta::decode(&mut &buf[..]).unwrap();
assert_eq!(original, decoded);
}
}
Loading