use crate::JournalMeta;
use alloy::{
consensus::Header,
primitives::{keccak256, Bytes, B256},
};
use std::sync::OnceLock;
use trevm::journal::{BundleStateIndex, JournalDecode, JournalDecodeError, JournalEncode};
#[derive(Debug, Clone)]
pub struct HostJournal<'a> {
meta: JournalMeta<'a>,
journal: BundleStateIndex<'a>,
serialized: OnceLock<Bytes>,
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> {
pub const fn new(meta: JournalMeta<'a>, journal: BundleStateIndex<'a>) -> Self {
Self { meta, journal, serialized: OnceLock::new(), hash: OnceLock::new() }
}
pub fn into_parts(self) -> (JournalMeta<'a>, BundleStateIndex<'a>) {
(self.meta, self.journal)
}
pub const fn meta(&self) -> &JournalMeta<'a> {
&self.meta
}
pub const fn journal(&self) -> &BundleStateIndex<'a> {
&self.journal
}
pub const fn host_height(&self) -> u64 {
self.meta.host_height()
}
pub const fn prev_journal_hash(&self) -> B256 {
self.meta.prev_journal_hash()
}
pub fn header(&self) -> &Header {
self.meta.header()
}
pub fn rollup_height(&self) -> u64 {
self.meta.rollup_height()
}
pub fn serialized(&self) -> &Bytes {
self.serialized.get_or_init(|| JournalEncode::encoded(self))
}
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), Cow::Owned(Header::default())),
make_state_diff(),
);
let buf = original.encoded();
let decoded = HostJournal::decode(&mut &buf[..]).unwrap();
assert_eq!(original, decoded);
}
}