use alloy_primitives::{B256, keccak256};
use derive_more::Display;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
#[display("OutputRootV0({}, {}, {})", state_root, bridge_storage_root, block_hash)]
pub struct OutputRoot {
pub state_root: B256,
pub bridge_storage_root: B256,
pub block_hash: B256,
}
impl OutputRoot {
pub const ENCODED_LENGTH: usize = 128;
pub const VERSION: u8 = 0;
pub const fn version(&self) -> B256 {
B256::ZERO
}
pub const fn from_parts(state_root: B256, bridge_storage_root: B256, block_hash: B256) -> Self {
Self { state_root, bridge_storage_root, block_hash }
}
pub fn encode(&self) -> [u8; Self::ENCODED_LENGTH] {
let mut encoded = [0u8; Self::ENCODED_LENGTH];
encoded[31] = Self::VERSION;
encoded[32..64].copy_from_slice(self.state_root.as_slice());
encoded[64..96].copy_from_slice(self.bridge_storage_root.as_slice());
encoded[96..128].copy_from_slice(self.block_hash.as_slice());
encoded
}
pub fn hash(&self) -> B256 {
keccak256(self.encode())
}
}
#[cfg(test)]
mod test {
use super::OutputRoot;
use alloy_primitives::{B256, Bytes, b256, bytes};
fn test_or() -> OutputRoot {
OutputRoot::from_parts(
B256::left_padding_from(&[0xbe, 0xef]),
B256::left_padding_from(&[0xba, 0xbe]),
B256::left_padding_from(&[0xc0, 0xde]),
)
}
#[test]
fn test_hash_output_root() {
const EXPECTED_HASH: B256 =
b256!("0c39fb6b07cf6694b13e63e59f7b15255be1c93a4d6d3e0da6c99729647c0d11");
let root = test_or();
assert_eq!(root.hash(), EXPECTED_HASH);
}
#[test]
fn test_encode_output_root() {
const EXPECTED_ENCODING: Bytes = bytes!(
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000beef000000000000000000000000000000000000000000000000000000000000babe000000000000000000000000000000000000000000000000000000000000c0de"
);
let root = OutputRoot::from_parts(
B256::left_padding_from(&[0xbe, 0xef]),
B256::left_padding_from(&[0xba, 0xbe]),
B256::left_padding_from(&[0xc0, 0xde]),
);
assert_eq!(root.encode().as_ref(), EXPECTED_ENCODING.as_ref());
}
}