diff --git a/Cargo.lock b/Cargo.lock
index 5731c35b..438c6f74 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2554,6 +2554,24 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "kona-proof-interop"
+version = "0.1.0"
+dependencies = [
+ "alloy-primitives",
+ "alloy-rlp",
+ "arbitrary",
+ "kona-interop",
+ "kona-preimage",
+ "kona-proof",
+ "maili-registry",
+ "op-alloy-genesis",
+ "rand",
+ "serde",
+ "serde_json",
+ "tracing",
+]
+
[[package]]
name = "kona-std-fpvm"
version = "0.1.2"
diff --git a/Cargo.toml b/Cargo.toml
index b55f19de..65fd3acf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -68,7 +68,9 @@ kona-client = { path = "bin/client", version = "0.1.0", default-features = false
kona-derive = { path = "crates/derive", version = "0.2.2", default-features = false }
kona-driver = { path = "crates/driver", version = "0.2.2", default-features = false }
kona-executor = { path = "crates/executor", version = "0.2.2", default-features = false }
+kona-interop = { path = "crates/interop", version = "0.1.0", default-features = false }
kona-proof = { path = "crates/proof-sdk/proof", version = "0.2.2", default-features = false }
+kona-proof-interop = { path = "crates/proof-sdk/proof-interop", version = "0.1.0", default-features = false }
kona-std-fpvm = { path = "crates/proof-sdk/std-fpvm", version = "0.1.2", default-features = false }
kona-preimage = { path = "crates/proof-sdk/preimage", version = "0.2.1", default-features = false }
kona-std-fpvm-proc = { path = "crates/proof-sdk/std-fpvm-proc", version = "0.1.2", default-features = false }
diff --git a/README.md b/README.md
index 75ca030b..3ead0735 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,7 @@ see the [SDK section of the book](https://op-rs.github.io/kona/sdk/intro.html).
**Proof SDK**
- [`kona-proof`](./crates/proof-sdk/proof): High level OP Stack state transition proof SDK.
+- [`kona-proof-interop`](./crates/proof-sdk/proof-interop): Extension of `kona-proof` with interop support.
- [`preimage`](./crates/proof-sdk/preimage): High level interfaces to the [`PreimageOracle`][fpp-specs] ABI.
- [`std-fpvm`](./crates/proof-sdk/std-fpvm): Platform specific [Fault Proof VM][g-fault-proof-vm] kernel APIs.
- [`std-fpvm-proc`](./crates/proof-sdk/std-fpvm-proc): Proc macro for [Fault Proof Program][fpp-specs] entrypoints.
diff --git a/crates/proof-sdk/proof-interop/Cargo.toml b/crates/proof-sdk/proof-interop/Cargo.toml
new file mode 100644
index 00000000..0820b9df
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/Cargo.toml
@@ -0,0 +1,45 @@
+[package]
+name = "kona-proof-interop"
+description = "OP Stack Proof SDK with Interop support"
+version = "0.1.0"
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+# Workspace
+kona-preimage.workspace = true
+kona-interop.workspace = true
+kona-proof.workspace = true
+
+# Maili
+maili-registry.workspace = true
+
+# Alloy
+alloy-rlp.workspace = true
+alloy-primitives.workspace = true
+
+# Op Alloy
+op-alloy-genesis = { workspace = true, features = ["serde"] }
+
+# General
+serde.workspace = true
+tracing.workspace = true
+serde_json.workspace = true
+
+# Arbitrary
+arbitrary = { version = "1.4", features = ["derive"], optional = true }
+
+[dev-dependencies]
+alloy-primitives = { workspace = true, features = ["rlp", "arbitrary"] }
+kona-interop = { workspace = true, features = ["arbitrary"] }
+arbitrary = { version = "1.4", features = ["derive"] }
+rand.workspace = true
+
+[features]
+arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary", "kona-interop/arbitrary"]
diff --git a/crates/proof-sdk/proof-interop/README.md b/crates/proof-sdk/proof-interop/README.md
new file mode 100644
index 00000000..4933aabb
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/README.md
@@ -0,0 +1,8 @@
+# `kona-proof-interop`
+
+
+
+
+
+
+`kona-proof-interop` is an OP Stack state transition proof SDK, with interop support, built on top of [`kona-proof`](../proof/)
diff --git a/crates/proof-sdk/proof-interop/src/boot.rs b/crates/proof-sdk/proof-interop/src/boot.rs
new file mode 100644
index 00000000..51d0349a
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/src/boot.rs
@@ -0,0 +1,123 @@
+//! This module contains the prologue phase of the client program, pulling in the boot information
+//! through the `PreimageOracle` ABI as local keys.
+
+use alloy_primitives::{B256, U256};
+use kona_preimage::{PreimageKey, PreimageOracleClient};
+use kona_proof::errors::OracleProviderError;
+use maili_registry::ROLLUP_CONFIGS;
+use op_alloy_genesis::RollupConfig;
+use serde::{Deserialize, Serialize};
+use tracing::warn;
+
+/// The local key ident for the L1 head hash.
+pub const L1_HEAD_KEY: U256 = U256::from_be_slice(&[1]);
+
+/// The local key ident for the agreed upon L2 pre-state claim.
+pub const L2_AGREED_PRE_STATE_KEY: U256 = U256::from_be_slice(&[2]);
+
+/// The local key ident for the L2 post-state claim.
+pub const L2_CLAIMED_POST_STATE_KEY: U256 = U256::from_be_slice(&[3]);
+
+/// The local key ident for the L2 claim timestamp.
+pub const L2_CLAIM_TIMESTAMP_KEY: U256 = U256::from_be_slice(&[4]);
+
+/// The local key ident for the L2 chain ID.
+pub const L2_CHAIN_ID_KEY: U256 = U256::from_be_slice(&[5]);
+
+/// The local key ident for the L2 rollup config.
+pub const L2_ROLLUP_CONFIG_KEY: U256 = U256::from_be_slice(&[6]);
+
+/// The boot information for the interop client program.
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct BootInfo {
+ /// The L1 head hash containing the safe L2 chain data that may reproduce the post-state claim.
+ pub l1_head: B256,
+ /// The agreed upon superchain pre-state commitment.
+ pub agreed_pre_state: B256,
+ /// The claimed (disputed) superchain post-state commitment.
+ pub claimed_post_state: B256,
+ /// The L2 claim timestamp.
+ pub claimed_l2_timestamp: u64,
+ /// The L2 chain ID.
+ pub chain_id: u64,
+ /// The rollup config for the L2 chain.
+ pub rollup_config: RollupConfig,
+}
+
+impl BootInfo {
+ /// Load the boot information from the preimage oracle.
+ ///
+ /// ## Takes
+ /// - `oracle`: The preimage oracle reader.
+ ///
+ /// ## Returns
+ /// - `Ok(BootInfo)`: The boot information.
+ /// - `Err(_)`: Failed to load the boot information.
+ pub async fn load(oracle: &O) -> Result
+ where
+ O: PreimageOracleClient + Send,
+ {
+ let mut l1_head: B256 = B256::ZERO;
+ oracle
+ .get_exact(PreimageKey::new_local(L1_HEAD_KEY.to()), l1_head.as_mut())
+ .await
+ .map_err(OracleProviderError::Preimage)?;
+
+ let mut l2_pre: B256 = B256::ZERO;
+ oracle
+ .get_exact(PreimageKey::new_local(L2_AGREED_PRE_STATE_KEY.to()), l2_pre.as_mut())
+ .await
+ .map_err(OracleProviderError::Preimage)?;
+
+ let mut l2_post: B256 = B256::ZERO;
+ oracle
+ .get_exact(PreimageKey::new_local(L2_CLAIMED_POST_STATE_KEY.to()), l2_post.as_mut())
+ .await
+ .map_err(OracleProviderError::Preimage)?;
+
+ let l2_claim_block = u64::from_be_bytes(
+ oracle
+ .get(PreimageKey::new_local(L2_CLAIM_TIMESTAMP_KEY.to()))
+ .await
+ .map_err(OracleProviderError::Preimage)?
+ .as_slice()
+ .try_into()
+ .map_err(OracleProviderError::SliceConversion)?,
+ );
+ let chain_id = u64::from_be_bytes(
+ oracle
+ .get(PreimageKey::new_local(L2_CHAIN_ID_KEY.to()))
+ .await
+ .map_err(OracleProviderError::Preimage)?
+ .as_slice()
+ .try_into()
+ .map_err(OracleProviderError::SliceConversion)?,
+ );
+
+ // Attempt to load the rollup config from the chain ID. If there is no config for the chain,
+ // fall back to loading the config from the preimage oracle.
+ let rollup_config = if let Some(config) = ROLLUP_CONFIGS.get(&chain_id) {
+ config.clone()
+ } else {
+ warn!(
+ target: "boot-loader",
+ "No rollup config found for chain ID {}, falling back to preimage oracle. This is insecure in production without additional validation!",
+ chain_id
+ );
+ let ser_cfg = oracle
+ .get(PreimageKey::new_local(L2_ROLLUP_CONFIG_KEY.to()))
+ .await
+ .map_err(OracleProviderError::Preimage)?;
+ serde_json::from_slice(&ser_cfg).map_err(OracleProviderError::Serde)?
+ };
+
+ Ok(Self {
+ l1_head,
+ agreed_pre_state: l2_pre,
+ claimed_post_state: l2_post,
+ claimed_l2_timestamp: l2_claim_block,
+ chain_id,
+ rollup_config,
+ })
+ }
+}
diff --git a/crates/proof-sdk/proof-interop/src/hint.rs b/crates/proof-sdk/proof-interop/src/hint.rs
new file mode 100644
index 00000000..c1a1d43f
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/src/hint.rs
@@ -0,0 +1,138 @@
+//! This module contains the [HintType] enum.
+
+use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+};
+use alloy_primitives::{hex, Bytes};
+use core::fmt::Display;
+use kona_proof::errors::HintParsingError;
+
+/// A [Hint] is parsed in the format ` `, where `` is a string that
+/// represents the type of hint, and `` is the data associated with the hint (bytes
+/// encoded as hex UTF-8).
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Hint {
+ /// The type of hint.
+ pub hint_type: HintType,
+ /// The data associated with the hint.
+ pub hint_data: Bytes,
+}
+
+impl Hint {
+ /// Parses a hint from a string.
+ pub fn parse(s: &str) -> Result {
+ let mut parts = s.split(' ').collect::>();
+
+ if parts.len() != 2 {
+ return Err(HintParsingError(alloc::format!("Invalid hint format: {}", s)));
+ }
+
+ let hint_type = HintType::try_from(parts.remove(0))?;
+ let hint_data =
+ hex::decode(parts.remove(0)).map_err(|e| HintParsingError(e.to_string()))?.into();
+
+ Ok(Self { hint_type, hint_data })
+ }
+
+ /// Splits the [Hint] into its components.
+ pub fn split(self) -> (HintType, Bytes) {
+ (self.hint_type, self.hint_data)
+ }
+}
+
+/// The [HintType] enum is used to specify the type of hint that was received.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum HintType {
+ /// A hint that specifies the block header of a layer 1 block.
+ L1BlockHeader,
+ /// A hint that specifies the transactions of a layer 1 block.
+ L1Transactions,
+ /// A hint that specifies the state node of a layer 1 block.
+ L1Receipts,
+ /// A hint that specifies a blob in the layer 1 beacon chain.
+ L1Blob,
+ /// A hint that specifies a precompile call on layer 1.
+ L1Precompile,
+ /// A hint that specifies the block header of a layer 2 block.
+ L2BlockHeader,
+ /// A hint that specifies the transactions of a layer 2 block.
+ L2Transactions,
+ /// A hint that specifies the code of a contract on layer 2.
+ L2Code,
+ /// A hint that specifies the preimage of the agreed upon pre-state claim.
+ AgreedPreState,
+ /// A hint that specifies the preimage of an L2 output root within the agreed upon pre-state,
+ /// by chain ID.
+ L2OutputRoot,
+ /// A hint that specifies the state node in the L2 state trie.
+ L2StateNode,
+ /// A hint that specifies the proof on the path to an account in the L2 state trie.
+ L2AccountProof,
+ /// A hint that specifies the proof on the path to a storage slot in an account within in the
+ /// L2 state trie.
+ L2AccountStorageProof,
+ /// A hint that specifies bulk storage of all the code, state and keys generated by an
+ /// execution witness.
+ L2PayloadWitness,
+}
+
+impl HintType {
+ /// Encodes the hint type as a string.
+ pub fn encode_with(&self, data: &[&[u8]]) -> String {
+ let concatenated = hex::encode(data.iter().copied().flatten().copied().collect::>());
+ alloc::format!("{} {}", self, concatenated)
+ }
+}
+
+impl TryFrom<&str> for HintType {
+ type Error = HintParsingError;
+
+ fn try_from(value: &str) -> Result {
+ match value {
+ "l1-block-header" => Ok(Self::L1BlockHeader),
+ "l1-transactions" => Ok(Self::L1Transactions),
+ "l1-receipts" => Ok(Self::L1Receipts),
+ "l1-blob" => Ok(Self::L1Blob),
+ "l1-precompile" => Ok(Self::L1Precompile),
+ "l2-block-header" => Ok(Self::L2BlockHeader),
+ "l2-transactions" => Ok(Self::L2Transactions),
+ "l2-code" => Ok(Self::L2Code),
+ "agreed-pre-state" => Ok(Self::AgreedPreState),
+ "l2-output-root" => Ok(Self::L2OutputRoot),
+ "l2-state-node" => Ok(Self::L2StateNode),
+ "l2-account-proof" => Ok(Self::L2AccountProof),
+ "l2-account-storage-proof" => Ok(Self::L2AccountStorageProof),
+ "l2-payload-witness" => Ok(Self::L2PayloadWitness),
+ _ => Err(HintParsingError(value.to_string())),
+ }
+ }
+}
+
+impl From for &str {
+ fn from(value: HintType) -> Self {
+ match value {
+ HintType::L1BlockHeader => "l1-block-header",
+ HintType::L1Transactions => "l1-transactions",
+ HintType::L1Receipts => "l1-receipts",
+ HintType::L1Blob => "l1-blob",
+ HintType::L1Precompile => "l1-precompile",
+ HintType::L2BlockHeader => "l2-block-header",
+ HintType::L2Transactions => "l2-transactions",
+ HintType::L2Code => "l2-code",
+ HintType::AgreedPreState => "agreed-pre-state",
+ HintType::L2OutputRoot => "l2-output-root",
+ HintType::L2StateNode => "l2-state-node",
+ HintType::L2AccountProof => "l2-account-proof",
+ HintType::L2AccountStorageProof => "l2-account-storage-proof",
+ HintType::L2PayloadWitness => "l2-payload-witness",
+ }
+ }
+}
+
+impl Display for HintType {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let s: &str = (*self).into();
+ write!(f, "{}", s)
+ }
+}
diff --git a/crates/proof-sdk/proof-interop/src/lib.rs b/crates/proof-sdk/proof-interop/src/lib.rs
new file mode 100644
index 00000000..4538a9a3
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/src/lib.rs
@@ -0,0 +1,16 @@
+#![doc = include_str!("../README.md")]
+#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)]
+#![deny(unused_must_use, rust_2018_idioms)]
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![cfg_attr(not(test), warn(unused_crate_dependencies))]
+#![cfg_attr(not(any(test, feature = "arbitrary")), no_std)]
+
+extern crate alloc;
+
+pub mod pre_state;
+
+mod hint;
+pub use hint::{Hint, HintType};
+
+pub mod boot;
+pub use boot::BootInfo;
diff --git a/crates/proof-sdk/proof-interop/src/pre_state.rs b/crates/proof-sdk/proof-interop/src/pre_state.rs
new file mode 100644
index 00000000..db173952
--- /dev/null
+++ b/crates/proof-sdk/proof-interop/src/pre_state.rs
@@ -0,0 +1,203 @@
+//! Types for the pre-state claims used in the interop proof.
+
+use alloc::vec::Vec;
+use alloy_primitives::{keccak256, B256};
+use alloy_rlp::{Buf, Decodable, Encodable, RlpDecodable, RlpEncodable};
+use kona_interop::{SuperRoot, SUPER_ROOT_VERSION};
+
+/// The current [TransitionState] encoding format version.
+pub const TRANSITION_STATE_VERSION: u8 = 255;
+
+/// The [PreState] of the interop proof program can be one of two types: a [SuperRoot] or a
+/// [TransitionState]. The [SuperRoot] is the canonical state of the superchain, while the
+/// [TransitionState] is a super-structure of the [SuperRoot] that represents the progress of a
+/// pending superchain state transition from one [SuperRoot] to the next.
+#[derive(Debug, Clone, Eq, PartialEq)]
+#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
+pub enum PreState {
+ /// The canonical state of the superchain.
+ SuperRoot(SuperRoot),
+ /// The progress of a pending superchain state transition.
+ TransitionState(TransitionState),
+}
+
+impl Encodable for PreState {
+ fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
+ match self {
+ Self::SuperRoot(super_root) => {
+ super_root.encode(out);
+ }
+ Self::TransitionState(transition_state) => {
+ transition_state.encode(out);
+ }
+ }
+ }
+}
+
+impl Decodable for PreState {
+ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result {
+ if buf.is_empty() {
+ return Err(alloy_rlp::Error::UnexpectedLength);
+ }
+
+ match buf[0] {
+ TRANSITION_STATE_VERSION => {
+ let transition_state = TransitionState::decode(buf)?;
+ Ok(Self::TransitionState(transition_state))
+ }
+ SUPER_ROOT_VERSION => {
+ let super_root =
+ SuperRoot::decode(buf).map_err(|_| alloy_rlp::Error::UnexpectedString)?;
+ Ok(Self::SuperRoot(super_root))
+ }
+ _ => Err(alloy_rlp::Error::Custom("invalid version byte")),
+ }
+ }
+}
+
+/// The [TransitionState] is a super-structure of the [SuperRoot] that represents the progress of a
+/// pending superchain state transition from one [SuperRoot] to the next.
+#[derive(Debug, Clone, Eq, PartialEq)]
+#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
+pub struct TransitionState {
+ /// The canonical pre-state super root commitment.
+ pub pre_state: SuperRoot,
+ /// The progress that has been made in the pending superchain state transition.
+ pub pending_progress: Vec,
+ /// The step number of the pending superchain state transition.
+ pub step: u64,
+}
+
+impl TransitionState {
+ /// Create a new [TransitionState] with the given pre-state, pending progress, and step number.
+ pub const fn new(
+ pre_state: SuperRoot,
+ pending_progress: Vec,
+ step: u64,
+ ) -> Self {
+ Self { pre_state, pending_progress, step }
+ }
+
+ /// Hashes the encoded [TransitionState] using [keccak256].
+ pub fn hash(&self) -> B256 {
+ let mut rlp_buf = Vec::with_capacity(self.length());
+ self.encode(&mut rlp_buf);
+ keccak256(&rlp_buf)
+ }
+}
+
+impl Encodable for TransitionState {
+ fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
+ out.put_u8(TRANSITION_STATE_VERSION);
+
+ // The pre-state has special encoding, since it is not RLP. We encode the structure, and
+ // then encode it as a RLP string.
+ let mut pre_state_buf = Vec::with_capacity(self.pre_state.encoded_length());
+ self.pre_state.encode(&mut pre_state_buf);
+ pre_state_buf.encode(out);
+
+ self.pending_progress.encode(out);
+ self.step.encode(out);
+ }
+
+ fn length(&self) -> usize {
+ self.pre_state.encoded_length() + self.pending_progress.length() + self.step.length()
+ }
+}
+
+impl Decodable for TransitionState {
+ fn decode(buf: &mut &[u8]) -> alloy_rlp::Result {
+ if buf.is_empty() {
+ return Err(alloy_rlp::Error::UnexpectedLength);
+ }
+
+ let version = buf[0];
+ if version != TRANSITION_STATE_VERSION {
+ return Err(alloy_rlp::Error::Custom("invalid version byte"));
+ }
+ buf.advance(1);
+
+ // The pre-state has special decoding, since it is not RLP. We decode the RLP string, and
+ // then decode the structure.
+ let pre_state_buf = Vec::::decode(buf)?;
+ let pre_state = SuperRoot::decode(&mut pre_state_buf.as_slice())
+ .map_err(|_| alloy_rlp::Error::UnexpectedString)?;
+
+ // The rest of the fields are RLP encoded as normal.
+ let pending_progress = Vec::::decode(buf)?;
+ let step = u64::decode(buf)?;
+
+ Ok(Self { pre_state, pending_progress, step })
+ }
+}
+
+/// A wrapper around a pending output root hash with the block hash it commits to.
+#[derive(Default, Debug, Clone, Eq, PartialEq, RlpEncodable, RlpDecodable)]
+#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
+pub struct OptimisticBlock {
+ /// The block hash of the output root.
+ pub block_hash: B256,
+ /// The output root hash.
+ pub output_root: B256,
+}
+
+impl OptimisticBlock {
+ /// Create a new [OptimisticBlock] with the given block hash and output root hash.
+ pub const fn new(block_hash: B256, output_root: B256) -> Self {
+ Self { block_hash, output_root }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::{OptimisticBlock, SuperRoot, TransitionState};
+ use alloy_primitives::B256;
+ use alloy_rlp::{Decodable, Encodable};
+ use arbitrary::Arbitrary;
+ use kona_interop::OutputRootWithChain;
+ use rand::Rng;
+
+ #[test]
+ fn test_static_transition_state_roundtrip() {
+ let transition_state = TransitionState::new(
+ SuperRoot::new(
+ 10,
+ vec![
+ (OutputRootWithChain::new(1, B256::default())),
+ (OutputRootWithChain::new(2, B256::default())),
+ ],
+ ),
+ vec![OptimisticBlock::default(), OptimisticBlock::default()],
+ 1,
+ );
+
+ let mut rlp_buf = Vec::with_capacity(transition_state.length());
+ transition_state.encode(&mut rlp_buf);
+
+ assert_eq!(transition_state, TransitionState::decode(&mut rlp_buf.as_slice()).unwrap());
+ }
+
+ #[test]
+ fn test_arbitrary_pre_state_roundtrip() {
+ let mut bytes = [0u8; 1024];
+ rand::thread_rng().fill(bytes.as_mut_slice());
+ let pre_state =
+ super::PreState::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
+
+ let mut rlp_buf = Vec::with_capacity(pre_state.length());
+ pre_state.encode(&mut rlp_buf);
+ assert_eq!(pre_state, super::PreState::decode(&mut rlp_buf.as_slice()).unwrap());
+ }
+
+ #[test]
+ fn test_arbitrary_transition_state_roundtrip() {
+ let mut bytes = [0u8; 1024];
+ rand::thread_rng().fill(bytes.as_mut_slice());
+ let transition_state =
+ TransitionState::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
+
+ let mut rlp_buf = Vec::with_capacity(transition_state.length());
+ transition_state.encode(&mut rlp_buf);
+ assert_eq!(transition_state, TransitionState::decode(&mut rlp_buf.as_slice()).unwrap());
+ }
+}