diff --git a/Cargo.lock b/Cargo.lock index c6c56c3cf..ae30cb92d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,9 +64,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +checksum = "8628f840dfddef51520dc2d5694e14e1d2098069de4ad9812964adb8309c1dc4" dependencies = [ "alloy-eips", "alloy-primitives", @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" +checksum = "eeffd2590ce780ddfaa9d0ae340eb2b4e08627650c4676eef537cef0b4bf535d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" +checksum = "a6a888c285255f82a60acecd06a719b84057d233ae281220532a8633e9220574" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" +checksum = "635d51aabd49374a3f2276acb4029fbc54c7436c69e4ee8a2c900ba7bbf03891" dependencies = [ "alloy-primitives", "alloy-serde", @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" +checksum = "ed9e5a6ea33a1b9c83c699f1cb476196e003d3b25d5afdd00b582655486b02ff" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" +checksum = "a1127a67e9995b8a33d06af5a06b79a87bc57e4f61276f21da5c5ed22afe3161" dependencies = [ "alloy-consensus", "alloy-eips", @@ -167,9 +167,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" +checksum = "efde0a98f2bd155538273430d6a50888544fe7c0df53b37bf8acf68805f4aa2a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1334a738aa1710cb8227441b3fcc319202ce78e967ef37406940242df4a454" +checksum = "8bcdf1fae99f4991dd5e843a49937d493573f2ccafa8e8ddb185b00042906c20" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -228,9 +228,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" +checksum = "28c67843e23cbf540e04cf5ea717f03d08d58299eaebaefbcc549f4e74b3e272" dependencies = [ "alloy-chains", "alloy-consensus", @@ -250,14 +250,17 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "parking_lot", "pin-project", "reqwest", + "schnellru", "serde", "serde_json", "thiserror 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", "tokio", "tracing", "url", + "wasmtimer", ] [[package]] @@ -284,9 +287,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" +checksum = "559c7a1056576ab63b2ba371ce8a066765509423531c0231a8a0d1c81457a5e7" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -302,13 +305,14 @@ dependencies = [ "tower", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06" +checksum = "6ff2186aa29bb0d5eb1e7239625b3553d0a6eff7194531c4a73210a284dbc2be" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -318,9 +322,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0285c4c09f838ab830048b780d7f4a4f460f309aa1194bb049843309524c64c" +checksum = "f6bbb60e151977865c3edd9b9a27b50c292da66e286fae461053ff9b2c6175ef" dependencies = [ "alloy-consensus", "alloy-eips", @@ -334,9 +338,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" +checksum = "fd7bc4a8d5a4a33168e35535ef261a408e0c72c17f469bcb1eae4c852734ba0f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -353,9 +357,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" +checksum = "e5b06099d7f448b55a418289d2d642f5e38c0207fc188c9cd7afe44382218a20" dependencies = [ "alloy-primitives", "serde", @@ -364,9 +368,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" +checksum = "bea89fb419de02a0e5222c0452b77701c5b52af74ec35334ac59e7b2b1a86c0f" dependencies = [ "alloy-primitives", "async-trait", @@ -436,9 +440,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" +checksum = "ac1c13d075c204fa4a3cba2a93555f9f5784a7412c1dbd3e4b652c29453d5dd2" dependencies = [ "alloy-json-rpc", "base64", @@ -451,13 +455,14 @@ dependencies = [ "tower", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-transport-http" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" +checksum = "c0038d95aaef539d9aed586d662914b232bd49ba9b4133670e514f5bb6d432bb" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -540,9 +545,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "arbitrary" @@ -1685,6 +1690,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.5" @@ -2041,7 +2052,6 @@ dependencies = [ "kona-executor", "kona-mpt", "kona-preimage", - "kona-primitives", "kona-providers", "lru", "op-alloy-consensus", @@ -2093,7 +2103,6 @@ dependencies = [ "anyhow", "async-trait", "brotli", - "kona-primitives", "kona-providers", "lazy_static", "miniz_oxide", @@ -2158,7 +2167,6 @@ dependencies = [ "kona-common", "kona-mpt", "kona-preimage", - "kona-primitives", "kona-providers-alloy", "op-alloy-genesis", "op-alloy-protocol", @@ -2215,22 +2223,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "kona-primitives" -version = "0.0.2" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "anyhow", - "c-kzg", - "revm", - "serde", - "serde_json", - "sha2", - "thiserror 1.0.64 (git+https://github.com/quartiq/thiserror?branch=no-std)", - "tracing", -] - [[package]] name = "kona-providers" version = "0.0.1" @@ -2255,11 +2247,11 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-client", + "alloy-serde", "alloy-transport", "alloy-transport-http", "async-trait", "kona-derive", - "kona-primitives", "kona-providers", "lazy_static", "lru", @@ -2268,6 +2260,7 @@ dependencies = [ "op-alloy-protocol", "prometheus", "reqwest", + "serde", "serde_json", "thiserror 1.0.64 (git+https://github.com/quartiq/thiserror?branch=no-std)", "tokio", @@ -2291,9 +2284,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.160" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -2650,9 +2643,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea7162170c6f3cad8f67f4dd7108e3f78349fd553da5b8bebff1e7ef8f38896" +checksum = "99d49163f952491820088dd0e66f3a35d63337c3066eceff0a931bf83a8e2101" dependencies = [ "alloy-consensus", "alloy-eips", @@ -2666,9 +2659,9 @@ dependencies = [ [[package]] name = "op-alloy-genesis" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3d31dfbbd8dd898c7512f8ce7d30103980485416f668566100b0ed0994b958" +checksum = "8e46c2ab105f679f0cbfbc3fb762f3456d4b8556c841e667fc8f3c2226eb6c1e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -2680,9 +2673,9 @@ dependencies = [ [[package]] name = "op-alloy-protocol" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310873e4fbfc41986716c4fb6000a8b49d025d932d2c261af58271c434b05288" +checksum = "6c439457b2a1791325603fc18a94cc175e0b4b1127f11ff8a45071f05d044dcb" dependencies = [ "alloy-consensus", "alloy-eips", @@ -2697,9 +2690,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349e7b420f45d1a00216ec4c65fcf3f0057a841bc39732c405c85ae782b94121" +checksum = "8a42a5ac4e07ed226b6a2aeefaad9b2cc7ec160e372ba626a4214d681a355fc2" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -3308,9 +3301,9 @@ dependencies = [ [[package]] name = "revm" -version = "14.0.3" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641702b12847f9ed418d552f4fcabe536d867a2c980e96b6e7e25d7b992f929f" +checksum = "34e44692d5736cc44c697a372e507890f8797f06d1541c5f4b9bec594d90fd8a" dependencies = [ "auto_impl", "cfg-if", @@ -3323,9 +3316,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "10.0.3" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5e14002afae20b5bf1566f22316122f42f57517000e559c55b25bf7a49cba2" +checksum = "6f89940d17d5d077570de1977f52f69049595322e237cb6c754c3d47f668f023" dependencies = [ "revm-primitives", "serde", @@ -3333,9 +3326,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "11.0.3" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3198c06247e8d4ad0d1312591edf049b0de4ddffa9fecb625c318fd67db8639b" +checksum = "d8f816aaea3245cbdbe7fdd84955df33597f9322c7912c3e3ba7bc855e03211f" dependencies = [ "aurora-engine-modexp", "blst", @@ -3353,9 +3346,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "10.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f1525851a03aff9a9d6a1d018b414d76252d6802ab54695b27093ecd7e7a101" +checksum = "532411bbde45a46707c1d434dcdc29866cf261c1b748fb01b303ce3b4310b361" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -3631,6 +3624,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schnellru" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3739,9 +3743,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2" dependencies = [ "indexmap", "itoa", @@ -4563,6 +4567,20 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "wasmtimer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.72" diff --git a/Cargo.toml b/Cargo.toml index 202e7d103..41c2a8383 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ kona-preimage = { path = "crates/preimage", version = "0.0.3" } kona-executor = { path = "crates/executor", version = "0.0.2" } kona-common-proc = { path = "crates/common-proc", version = "0.0.3" } kona-derive = { path = "crates/derive", version = "0.0.3", default-features = false } -kona-primitives = { path = "crates/primitives", version = "0.0.2", default-features = false } kona-providers = { path = "crates/providers", version = "0.0.1" } kona-providers-alloy = { path = "crates/providers-alloy", version = "0.0.1", default-features = false } @@ -111,7 +110,7 @@ serde_json = { version = "1.0.128", default-features = false } # Ethereum unsigned-varint = "0.8.0" -revm = { version = "14.0.3", default-features = false } +revm = { version = "16.0.0", default-features = false } # Optimism superchain = { version = "0.7", default-features = false } @@ -122,19 +121,20 @@ rocksdb = { version = "0.22", default-features = false, features = ["snappy"] } # Alloy alloy-rlp = { version = "0.3.8", default-features = false } alloy-trie = { version = "0.7.2", default-features = false } -alloy-eips = { version = "0.4.2", default-features = false } -alloy-provider = { version = "0.4.2", default-features = false } +alloy-eips = { version = "0.5.0", default-features = false } +alloy-serde = { version = "0.5.0", default-features = false } +alloy-provider = { version = "0.5.0", default-features = false } alloy-primitives = { version = "0.8", default-features = false } -alloy-consensus = { version = "0.4.2", default-features = false } -alloy-transport = { version = "0.4.2", default-features = false } -alloy-rpc-types = { version = "0.4.2", default-features = false } -alloy-rpc-client = { version = "0.4.2", default-features = false } -alloy-rpc-types-engine = { version = "0.4.2", default-features = false } -alloy-node-bindings = { version = "0.4.2", default-features = false } -alloy-transport-http = { version = "0.4.2", default-features = false } +alloy-consensus = { version = "0.5.0", default-features = false } +alloy-transport = { version = "0.5.0", default-features = false } +alloy-rpc-types = { version = "0.5.0", default-features = false } +alloy-rpc-client = { version = "0.5.0", default-features = false } +alloy-rpc-types-engine = { version = "0.5.0", default-features = false } +alloy-node-bindings = { version = "0.5.0", default-features = false } +alloy-transport-http = { version = "0.5.0", default-features = false } # OP Alloy -op-alloy-consensus = { version = "0.4.0", default-features = false } -op-alloy-protocol = { version = "0.4.0", default-features = false } -op-alloy-genesis = { version = "0.4.0", default-features = false } -op-alloy-rpc-types-engine = { version = "0.4.0", default-features = false } +op-alloy-consensus = { version = "0.5.0", default-features = false } +op-alloy-protocol = { version = "0.5.0", default-features = false } +op-alloy-genesis = { version = "0.5.0", default-features = false } +op-alloy-rpc-types-engine = { version = "0.5.0", default-features = false } diff --git a/bin/client/Cargo.toml b/bin/client/Cargo.toml index 4a2fcaa3d..ffe47631d 100644 --- a/bin/client/Cargo.toml +++ b/bin/client/Cargo.toml @@ -17,7 +17,6 @@ kona-mpt.workspace = true kona-derive.workspace = true kona-executor.workspace = true kona-providers.workspace = true -kona-primitives = { workspace = true, features = ["serde"] } op-alloy-genesis = { workspace = true, features = ["serde"] } op-alloy-protocol.workspace = true op-alloy-rpc-types-engine.workspace = true diff --git a/bin/client/src/l1/blob_provider.rs b/bin/client/src/l1/blob_provider.rs index 5fe636339..c909ea8ad 100644 --- a/bin/client/src/l1/blob_provider.rs +++ b/bin/client/src/l1/blob_provider.rs @@ -3,13 +3,12 @@ use crate::HintType; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_consensus::Blob; -use alloy_eips::eip4844::FIELD_ELEMENTS_PER_BLOB; +use alloy_eips::{eip1898::NumHash, eip4844::FIELD_ELEMENTS_PER_BLOB}; use alloy_primitives::keccak256; use anyhow::Result; use async_trait::async_trait; use kona_derive::traits::BlobProvider; use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; -use kona_primitives::IndexedBlobHash; use op_alloy_protocol::BlockInfo; /// An oracle-backed blob provider. @@ -33,10 +32,10 @@ impl OracleBlobProvider { /// ## Returns /// - `Ok(blob)`: The blob. /// - `Err(e)`: The blob could not be retrieved. - async fn get_blob(&self, block_ref: &BlockInfo, blob_hash: &IndexedBlobHash) -> Result { + async fn get_blob(&self, block_ref: &BlockInfo, blob_hash: &NumHash) -> Result { let mut blob_req_meta = [0u8; 48]; blob_req_meta[0..32].copy_from_slice(blob_hash.hash.as_ref()); - blob_req_meta[32..40].copy_from_slice((blob_hash.index as u64).to_be_bytes().as_ref()); + blob_req_meta[32..40].copy_from_slice((blob_hash.number).to_be_bytes().as_ref()); blob_req_meta[40..48].copy_from_slice(block_ref.timestamp.to_be_bytes().as_ref()); // Send a hint for the blob commitment and field elements. @@ -78,11 +77,11 @@ impl BlobProvider for OracleBlobProvider { async fn get_blobs( &mut self, block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, Self::Error> { + blob_hashes: &[NumHash], + ) -> Result>, Self::Error> { let mut blobs = Vec::with_capacity(blob_hashes.len()); for hash in blob_hashes { - blobs.push(self.get_blob(block_ref, hash).await?); + blobs.push(Box::new(self.get_blob(block_ref, hash).await?)); } Ok(blobs) } diff --git a/bin/host/Cargo.toml b/bin/host/Cargo.toml index 67288674b..d0fc65d5a 100644 --- a/bin/host/Cargo.toml +++ b/bin/host/Cargo.toml @@ -18,7 +18,6 @@ kona-client.workspace = true kona-common.workspace = true kona-preimage.workspace = true kona-providers-alloy.workspace = true -kona-primitives = { workspace = true, features = ["online"] } # Alloy & Revm alloy-eips.workspace = true diff --git a/bin/host/src/fetcher/mod.rs b/bin/host/src/fetcher/mod.rs index 2db12f9ac..cbb9dde11 100644 --- a/bin/host/src/fetcher/mod.rs +++ b/bin/host/src/fetcher/mod.rs @@ -3,7 +3,9 @@ use crate::{kv::KeyValueStore, util}; use alloy_consensus::{Header, TxEnvelope, EMPTY_ROOT_HASH}; -use alloy_eips::{eip2718::Encodable2718, eip4844::FIELD_ELEMENTS_PER_BLOB, BlockId}; +use alloy_eips::{ + eip1898::NumHash, eip2718::Encodable2718, eip4844::FIELD_ELEMENTS_PER_BLOB, BlockId, +}; use alloy_primitives::{address, keccak256, Address, Bytes, B256}; use alloy_provider::{Provider, ReqwestProvider}; use alloy_rlp::{Decodable, EMPTY_STRING_CODE}; @@ -13,7 +15,6 @@ use alloy_rpc_types::{ use anyhow::{anyhow, Result}; use kona_client::HintType; use kona_preimage::{PreimageKey, PreimageKeyType}; -use kona_primitives::IndexedBlobHash; use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; use op_alloy_protocol::BlockInfo; use std::sync::Arc; @@ -187,7 +188,7 @@ where let timestamp = u64::from_be_bytes(timestamp_data_bytes); let partial_block_ref = BlockInfo { timestamp, ..Default::default() }; - let indexed_hash = IndexedBlobHash { index: index as usize, hash }; + let indexed_hash = NumHash { number: index, hash }; // Fetch the blob sidecar from the blob provider. let mut sidecars = self diff --git a/book/src/sdk/fpvm-backend.md b/book/src/sdk/fpvm-backend.md index 909113a99..c0da6bda3 100644 --- a/book/src/sdk/fpvm-backend.md +++ b/book/src/sdk/fpvm-backend.md @@ -5,7 +5,7 @@ Kona is effectively split into two parts: -- OP Stack state transition logic & types (`kona-derive`, `kona-executor`, `kona-mpt`, `kona-primitives`) +- OP Stack state transition logic (`kona-derive`, `kona-executor`, `kona-mpt`) - {{#template ../../templates/glossary-link.md root=./ ref=fault-proof-vm text=Fault Proof VM}} IO and utilities (`kona-common`, `kona-common-proc`, `kona-preimage`) diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index 7e2d8d518..4a9b1655e 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -34,7 +34,6 @@ async-trait.workspace = true # Workspace kona-providers.workspace = true -kona-primitives.workspace = true # `serde` feature dependencies serde = { workspace = true, optional = true } @@ -72,7 +71,6 @@ metrics = [ ] serde = [ "dep:serde", - "kona-primitives/serde", "alloy-primitives/serde", "alloy-consensus/serde", "op-alloy-consensus/serde", diff --git a/crates/derive/src/attributes/mod.rs b/crates/derive/src/attributes/mod.rs index 7b9b05e69..509806268 100644 --- a/crates/derive/src/attributes/mod.rs +++ b/crates/derive/src/attributes/mod.rs @@ -647,7 +647,7 @@ mod tests { prev_randao, suggested_fee_recipient: SEQUENCER_FEE_VAULT_ADDRESS, parent_beacon_block_root, - withdrawals: None, + withdrawals: Some(vec![]), }, transactions: payload.transactions.clone(), no_tx_pool: Some(true), @@ -693,8 +693,8 @@ mod tests { timestamp: next_l2_time, prev_randao, suggested_fee_recipient: SEQUENCER_FEE_VAULT_ADDRESS, - parent_beacon_block_root: None, - withdrawals: None, + parent_beacon_block_root: Some(B256::ZERO), + withdrawals: Some(vec![]), }, transactions: payload.transactions.clone(), no_tx_pool: Some(true), @@ -703,7 +703,7 @@ mod tests { )), eip_1559_params: None, }; + assert_eq!(payload.transactions.as_ref().unwrap().len(), 10); assert_eq!(payload, expected); - assert_eq!(payload.transactions.unwrap().len(), 4); } } diff --git a/crates/derive/src/batch/span_batch/transactions.rs b/crates/derive/src/batch/span_batch/transactions.rs index 905140673..41967e34c 100644 --- a/crates/derive/src/batch/span_batch/transactions.rs +++ b/crates/derive/src/batch/span_batch/transactions.rs @@ -8,7 +8,7 @@ use super::{ use alloc::vec::Vec; use alloy_consensus::{Transaction, TxEnvelope, TxType}; use alloy_eips::eip2718::Encodable2718; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_primitives::{Address, Bytes, U256}; use alloy_rlp::{Buf, Decodable, Encodable}; /// This struct contains the decoded information for transactions in a span batch. @@ -348,11 +348,11 @@ impl SpanBatchTransactions { let signature_v = signature.v().to_u64(); let y_parity_bit = convert_v_to_y_parity(signature_v, tx_type)?; let contract_creation_bit = match to { - TxKind::Call(address) => { + Some(address) => { self.tx_tos.push(address); 0 } - TxKind::Create => 1, + None => 1, }; let mut tx_data_buf = Vec::new(); span_batch_tx.encode(&mut tx_data_buf); @@ -374,7 +374,7 @@ impl SpanBatchTransactions { mod tests { use super::*; use alloy_consensus::{Signed, TxEip1559, TxEip2930, TxLegacy}; - use alloy_primitives::{address, Signature}; + use alloy_primitives::{address, Signature, TxKind}; #[test] fn test_span_batch_transactions_add_empty_txs() { diff --git a/crates/derive/src/errors.rs b/crates/derive/src/errors.rs index da7cb19d9..330c66386 100644 --- a/crates/derive/src/errors.rs +++ b/crates/derive/src/errors.rs @@ -7,11 +7,27 @@ use crate::{ use alloc::string::String; use alloy_eips::BlockNumHash; use alloy_primitives::B256; -use kona_primitives::BlobDecodingError; use op_alloy_genesis::system::SystemConfigUpdateError; use op_alloy_protocol::DepositError; use thiserror::Error; +/// Blob Decuding Error +#[derive(Error, Debug, PartialEq, Eq)] +pub enum BlobDecodingError { + /// Invalid field element + #[error("Invalid field element")] + InvalidFieldElement, + /// Invalid encoding version + #[error("Invalid encoding version")] + InvalidEncodingVersion, + /// Invalid length + #[error("Invalid length")] + InvalidLength, + /// Missing Data + #[error("Missing data")] + MissingData, +} + /// A result type for the derivation pipeline stages. pub type PipelineResult = Result; diff --git a/crates/derive/src/sources/blobs.rs b/crates/derive/src/sources/blobs.rs index 65266e207..54cffdb51 100644 --- a/crates/derive/src/sources/blobs.rs +++ b/crates/derive/src/sources/blobs.rs @@ -1,18 +1,181 @@ //! Blob Data Source use crate::{ - errors::{BlobProviderError, PipelineError, PipelineResult}, + errors::{BlobDecodingError, BlobProviderError, PipelineError, PipelineResult}, traits::{AsyncIterator, BlobProvider}, }; -use alloc::{boxed::Box, format, string::ToString, vec::Vec}; +use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec}; use alloy_consensus::{Transaction, TxEip4844Variant, TxEnvelope, TxType}; -use alloy_primitives::{Address, Bytes, TxKind}; +use alloy_eips::{ + eip1898::NumHash, + eip4844::{Blob, BYTES_PER_BLOB, VERSIONED_HASH_VERSION_KZG}, +}; +use alloy_primitives::{Address, Bytes}; use async_trait::async_trait; -use kona_primitives::{BlobData, IndexedBlobHash}; use kona_providers::ChainProvider; use op_alloy_protocol::BlockInfo; use tracing::warn; +/// The blob encoding version +pub(crate) const BLOB_ENCODING_VERSION: u8 = 0; + +/// Maximum blob data size +pub(crate) const BLOB_MAX_DATA_SIZE: usize = (4 * 31 + 3) * 1024 - 4; // 130044 + +/// Blob Encoding/Decoding Rounds +pub(crate) const BLOB_ENCODING_ROUNDS: usize = 1024; + +/// The Blob Data +#[derive(Default, Clone, Debug)] +pub struct BlobData { + /// The blob data + pub(crate) data: Option, + /// The calldata + pub(crate) calldata: Option, +} + +impl BlobData { + /// Decodes the blob into raw byte data. + /// Returns a [BlobDecodingError] if the blob is invalid. + pub(crate) fn decode(&self) -> Result { + let data = self.data.as_ref().ok_or(BlobDecodingError::MissingData)?; + + // Validate the blob encoding version + if data[VERSIONED_HASH_VERSION_KZG as usize] != BLOB_ENCODING_VERSION { + return Err(BlobDecodingError::InvalidEncodingVersion); + } + + // Decode the 3 byte big endian length value into a 4 byte integer + let length = u32::from_be_bytes([0, data[2], data[3], data[4]]) as usize; + + // Validate the length + if length > BLOB_MAX_DATA_SIZE { + return Err(BlobDecodingError::InvalidLength); + } + + // Round 0 copies the remaining 27 bytes of the first field element + let mut output = vec![0u8; BLOB_MAX_DATA_SIZE]; + output[0..27].copy_from_slice(&data[5..32]); + + // Process the remaining 3 field elements to complete round 0 + let mut output_pos = 28; + let mut input_pos = 32; + let mut encoded_byte = [0u8; 4]; + encoded_byte[0] = data[0]; + + for b in encoded_byte.iter_mut().skip(1) { + let (enc, opos, ipos) = + self.decode_field_element(output_pos, input_pos, &mut output)?; + *b = enc; + output_pos = opos; + input_pos = ipos; + } + + // Reassemble the 4 by 6 bit encoded chunks into 3 bytes of output + output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output); + + // In each remaining round, decode 4 field elements (128 bytes) of the + // input into 127 bytes of output + for _ in 1..BLOB_ENCODING_ROUNDS { + // Break early if the output position is greater than the length + if output_pos >= length { + break; + } + + for d in &mut encoded_byte { + let (enc, opos, ipos) = + self.decode_field_element(output_pos, input_pos, &mut output)?; + *d = enc; + output_pos = opos; + input_pos = ipos; + } + output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output); + } + + // Validate the remaining bytes + for o in output.iter().skip(length) { + if *o != 0u8 { + return Err(BlobDecodingError::InvalidFieldElement); + } + } + + // Validate the remaining bytes + output.truncate(length); + for i in input_pos..BYTES_PER_BLOB { + if data[i] != 0 { + return Err(BlobDecodingError::InvalidFieldElement); + } + } + + Ok(Bytes::from(output)) + } + + /// Decodes the next input field element by writing its lower 31 bytes into its + /// appropriate place in the output and checking the high order byte is valid. + /// Returns a [BlobDecodingError] if a field element is seen with either of its + /// two high order bits set. + pub(crate) fn decode_field_element( + &self, + output_pos: usize, + input_pos: usize, + output: &mut [u8], + ) -> Result<(u8, usize, usize), BlobDecodingError> { + let Some(data) = self.data.as_ref() else { + return Err(BlobDecodingError::MissingData); + }; + + // two highest order bits of the first byte of each field element should always be 0 + if data[input_pos] & 0b1100_0000 != 0 { + return Err(BlobDecodingError::InvalidFieldElement); + } + output[output_pos..output_pos + 31].copy_from_slice(&data[input_pos + 1..input_pos + 32]); + Ok((data[input_pos], output_pos + 32, input_pos + 32)) + } + + /// Reassemble 4 by 6 bit encoded chunks into 3 bytes of output and place them in their + /// appropriate output positions. + pub(crate) fn reassemble_bytes( + &self, + mut output_pos: usize, + encoded_byte: &[u8], + output: &mut [u8], + ) -> usize { + output_pos -= 1; + let x = (encoded_byte[0] & 0b0011_1111) | ((encoded_byte[1] & 0b0011_0000) << 2); + let y = (encoded_byte[1] & 0b0000_1111) | ((encoded_byte[3] & 0b0000_1111) << 4); + let z = (encoded_byte[2] & 0b0011_1111) | ((encoded_byte[3] & 0b0011_0000) << 2); + output[output_pos - 32] = z; + output[output_pos - (32 * 2)] = y; + output[output_pos - (32 * 3)] = x; + output_pos + } + + /// Fills in the pointers to the fetched blob bodies. + /// There should be exactly one placeholder blobOrCalldata + /// element for each blob, otherwise an error is returned. + pub(crate) fn fill( + &mut self, + blobs: &[Box], + index: usize, + ) -> Result<(), BlobDecodingError> { + // Do not fill if there is no calldata to fill + if self.calldata.as_ref().map_or(false, |data| data.is_empty()) { + return Ok(()); + } + + if index >= blobs.len() { + return Err(BlobDecodingError::InvalidLength); + } + + if blobs[index].is_empty() { + return Err(BlobDecodingError::MissingData); + } + + self.data = Some(Bytes::from(*blobs[index])); + Ok(()) + } +} + /// A data iterator that reads from a blob. #[derive(Debug, Clone)] pub struct BlobSource @@ -60,8 +223,8 @@ where } } - fn extract_blob_data(&self, txs: Vec) -> (Vec, Vec) { - let mut index = 0; + fn extract_blob_data(&self, txs: Vec) -> (Vec, Vec) { + let mut number: u64 = 0; let mut data = Vec::new(); let mut hashes = Vec::new(); for tx in txs { @@ -80,14 +243,14 @@ where }, _ => continue, }; - let TxKind::Call(to) = tx_kind else { continue }; + let Some(to) = tx_kind else { continue }; if to != self.batcher_address { - index += blob_hashes.map_or(0, |h| h.len()); + number += blob_hashes.map_or(0, |h| h.len() as u64); continue; } if tx.recover_signer().unwrap_or_default() != self.signer { - index += blob_hashes.map_or(0, |h| h.len()); + number += blob_hashes.map_or(0, |h| h.len() as u64); continue; } if tx.tx_type() != TxType::Eip4844 { @@ -111,10 +274,10 @@ where continue; }; for blob in blob_hashes { - let indexed = IndexedBlobHash { hash: blob, index }; + let indexed = NumHash { hash: blob, number }; hashes.push(indexed); data.push(BlobData::default()); - index += 1; + number += 1; } } (data, hashes) @@ -219,6 +382,70 @@ pub(crate) mod tests { use alloy_rlp::Decodable; use kona_providers::test_utils::TestChainProvider; + #[test] + fn test_reassemble_bytes() { + let blob_data = BlobData::default(); + let mut output = vec![0u8; 128]; + let encoded_byte = [0x00, 0x00, 0x00, 0x00]; + let output_pos = blob_data.reassemble_bytes(127, &encoded_byte, &mut output); + assert_eq!(output_pos, 126); + assert_eq!(output, vec![0u8; 128]); + } + + #[test] + fn test_cannot_fill_empty_calldata() { + let mut blob_data = BlobData { calldata: Some(Bytes::new()), ..Default::default() }; + let blobs = vec![Box::new(Blob::with_last_byte(1u8))]; + assert_eq!(blob_data.fill(&blobs, 0), Ok(())); + } + + #[test] + fn test_fill_oob_index() { + let mut blob_data = BlobData::default(); + let blobs = vec![Box::new(Blob::with_last_byte(1u8))]; + assert_eq!(blob_data.fill(&blobs, 1), Err(BlobDecodingError::InvalidLength)); + } + + #[test] + #[ignore] + fn test_fill_empty_blob() { + let mut blob_data = BlobData::default(); + let blobs = vec![Box::new(Blob::ZERO)]; + assert_eq!(blob_data.fill(&blobs, 0), Err(BlobDecodingError::MissingData)); + } + + #[test] + fn test_fill_blob() { + let mut blob_data = BlobData::default(); + let blobs = vec![Box::new(Blob::with_last_byte(1u8))]; + assert_eq!(blob_data.fill(&blobs, 0), Ok(())); + let expected = Bytes::from([&[0u8; 131071][..], &[1u8]].concat()); + assert_eq!(blob_data.data, Some(expected)); + } + + #[test] + fn test_blob_data_decode_missing_data() { + let blob_data = BlobData::default(); + assert_eq!(blob_data.decode(), Err(BlobDecodingError::MissingData)); + } + + #[test] + fn test_blob_data_decode_invalid_encoding_version() { + let blob_data = BlobData { data: Some(Bytes::from(vec![1u8; 32])), ..Default::default() }; + assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidEncodingVersion)); + } + + #[test] + fn test_blob_data_decode_invalid_length() { + let mut data = vec![0u8; 32]; + data[VERSIONED_HASH_VERSION_KZG as usize] = BLOB_ENCODING_VERSION; + data[2] = 0xFF; + data[3] = 0xFF; + data[4] = 0xFF; + let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() }; + assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidLength)); + } + pub(crate) fn default_test_blob_source() -> BlobSource { let chain_provider = TestChainProvider::default(); let blob_fetcher = TestBlobProvider::default(); diff --git a/crates/derive/src/sources/calldata.rs b/crates/derive/src/sources/calldata.rs index 3d8b1205e..8c5adf529 100644 --- a/crates/derive/src/sources/calldata.rs +++ b/crates/derive/src/sources/calldata.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloc::{boxed::Box, collections::VecDeque, format}; use alloy_consensus::{Transaction, TxEnvelope}; -use alloy_primitives::{Address, Bytes, TxKind}; +use alloy_primitives::{Address, Bytes}; use async_trait::async_trait; use kona_providers::ChainProvider; use op_alloy_protocol::BlockInfo; @@ -67,7 +67,7 @@ impl CalldataSource { TxEnvelope::Eip1559(tx) => (tx.tx().to(), tx.tx().input()), _ => return None, }; - let TxKind::Call(to) = tx_kind else { return None }; + let to = tx_kind?; if to != self.batch_inbox_address { return None; @@ -106,7 +106,7 @@ mod tests { use super::*; use crate::errors::PipelineErrorKind; use alloy_consensus::{Signed, TxEip2930, TxEip4844, TxEip4844Variant, TxLegacy}; - use alloy_primitives::{address, Address, Signature}; + use alloy_primitives::{address, Address, Signature, TxKind}; use kona_providers::test_utils::TestChainProvider; pub(crate) fn test_legacy_tx(to: Address) -> TxEnvelope { diff --git a/crates/derive/src/sources/mod.rs b/crates/derive/src/sources/mod.rs index 253b65268..41e1b7748 100644 --- a/crates/derive/src/sources/mod.rs +++ b/crates/derive/src/sources/mod.rs @@ -4,7 +4,7 @@ mod ethereum; pub use ethereum::EthereumDataSource; mod blobs; -pub use blobs::BlobSource; +pub use blobs::{BlobData, BlobSource}; mod calldata; pub use calldata::CalldataSource; diff --git a/crates/derive/src/sources/variant.rs b/crates/derive/src/sources/variant.rs index 32036f791..be38a9fae 100644 --- a/crates/derive/src/sources/variant.rs +++ b/crates/derive/src/sources/variant.rs @@ -44,11 +44,13 @@ where mod tests { use super::*; - use kona_primitives::BlobData; use kona_providers::test_utils::TestChainProvider; use op_alloy_protocol::BlockInfo; - use crate::{sources::EthereumDataSourceVariant, traits::test_utils::TestBlobProvider}; + use crate::{ + sources::{BlobData, EthereumDataSourceVariant}, + traits::test_utils::TestBlobProvider, + }; #[tokio::test] async fn test_variant_next_calldata() { diff --git a/crates/derive/src/traits/data_sources.rs b/crates/derive/src/traits/data_sources.rs index 265324d2b..97d32c48c 100644 --- a/crates/derive/src/traits/data_sources.rs +++ b/crates/derive/src/traits/data_sources.rs @@ -3,11 +3,10 @@ use crate::errors::PipelineResult; use alloc::{boxed::Box, fmt::Debug, vec::Vec}; -use alloy_eips::eip4844::Blob; +use alloy_eips::{eip1898::NumHash, eip4844::Blob}; use alloy_primitives::Bytes; use async_trait::async_trait; use core::fmt::Display; -use kona_primitives::IndexedBlobHash; use op_alloy_protocol::BlockInfo; /// The BlobProvider trait specifies the functionality of a data source that can provide blobs. @@ -20,8 +19,8 @@ pub trait BlobProvider { async fn get_blobs( &mut self, block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, Self::Error>; + blob_hashes: &[NumHash], + ) -> Result>, Self::Error>; } /// Describes the functionality of a data source that can provide data availability information. diff --git a/crates/derive/src/traits/test_utils.rs b/crates/derive/src/traits/test_utils.rs index 145591d56..8992fa702 100644 --- a/crates/derive/src/traits/test_utils.rs +++ b/crates/derive/src/traits/test_utils.rs @@ -5,12 +5,11 @@ use crate::{ traits::{AsyncIterator, BlobProvider, DataAvailabilityProvider}, }; use alloc::{boxed::Box, vec, vec::Vec}; -use alloy_eips::eip4844::Blob; +use alloy_eips::{eip1898::NumHash, eip4844::Blob}; use alloy_primitives::{map::HashMap, Address, Bytes, B256}; use anyhow::Result; use async_trait::async_trait; use core::fmt::Debug; -use kona_primitives::IndexedBlobHash; use op_alloy_protocol::BlockInfo; /// Mock data iterator @@ -84,15 +83,15 @@ impl BlobProvider for TestBlobProvider { async fn get_blobs( &mut self, _block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, Self::Error> { + blob_hashes: &[NumHash], + ) -> Result>, Self::Error> { if self.should_error { return Err(BlobProviderError::SlotDerivation); } let mut blobs = Vec::new(); for blob_hash in blob_hashes { if let Some(data) = self.blobs.get(&blob_hash.hash) { - blobs.push(*data); + blobs.push(Box::new(*data)); } } Ok(blobs) diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 709d5063d..baca95bd5 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -331,7 +331,7 @@ where state_root, transactions_root, receipts_root, - requests_root: None, + requests_hash: None, withdrawals_root, logs_bloom, difficulty: U256::ZERO, diff --git a/crates/primitives/CHANGELOG.md b/crates/primitives/CHANGELOG.md deleted file mode 100644 index 654086147..000000000 --- a/crates/primitives/CHANGELOG.md +++ /dev/null @@ -1,55 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.0.2](https://github.com/anton-rs/kona/compare/kona-primitives-v0.0.1...kona-primitives-v0.0.2) - 2024-09-04 - -### Added -- update superchain registry deps ([#463](https://github.com/anton-rs/kona/pull/463)) -- *(primitives)* `serde` for `L1BlockInfoTx` ([#460](https://github.com/anton-rs/kona/pull/460)) - -### Fixed -- *(examples)* Revm Features ([#482](https://github.com/anton-rs/kona/pull/482)) -- *(workspace)* Use published `revm` version ([#459](https://github.com/anton-rs/kona/pull/459)) -- downgrade for release plz ([#458](https://github.com/anton-rs/kona/pull/458)) -- *(workspace)* Add Unused Dependency Lint ([#453](https://github.com/anton-rs/kona/pull/453)) -- fix superchain registry + primitives versions ([#425](https://github.com/anton-rs/kona/pull/425)) -- *(derive)* Granite Hardfork Support ([#420](https://github.com/anton-rs/kona/pull/420)) -- *(deps)* Bump Alloy Dependencies ([#409](https://github.com/anton-rs/kona/pull/409)) -- pin two dependencies due to upstream semver issues ([#391](https://github.com/anton-rs/kona/pull/391)) - -### Other -- *(workspace)* Alloy Version Bumps ([#467](https://github.com/anton-rs/kona/pull/467)) -- *(workspace)* Update for `anton-rs` org transfer ([#474](https://github.com/anton-rs/kona/pull/474)) -- *(workspace)* Hoist Dependencies ([#466](https://github.com/anton-rs/kona/pull/466)) -- *(bin)* Remove `kt` ([#461](https://github.com/anton-rs/kona/pull/461)) -- refactor types out of kona-derive ([#454](https://github.com/anton-rs/kona/pull/454)) -- bump scr version ([#440](https://github.com/anton-rs/kona/pull/440)) -- Bump `superchain-registry` version ([#306](https://github.com/anton-rs/kona/pull/306)) - -## [0.0.1](https://github.com/anton-rs/kona/releases/tag/kona-primitives-v0.0.1) - 2024-06-22 - -### Added -- *(kona-derive)* Towards Derivation ([#243](https://github.com/anton-rs/kona/pull/243)) -- *(ci)* Dependabot config ([#236](https://github.com/anton-rs/kona/pull/236)) -- *(client)* `StatelessL2BlockExecutor` ([#210](https://github.com/anton-rs/kona/pull/210)) -- *(primitives)* move attributes into primitives ([#163](https://github.com/anton-rs/kona/pull/163)) -- *(plasma)* Implements Plasma Support for kona derive ([#152](https://github.com/anton-rs/kona/pull/152)) -- *(primitives)* kona-derive type refactor ([#135](https://github.com/anton-rs/kona/pull/135)) - -### Fixed -- use 2718 encoding ([#231](https://github.com/anton-rs/kona/pull/231)) -- Strong Error Typing ([#187](https://github.com/anton-rs/kona/pull/187)) -- *(primitives)* use decode_2718() to gracefully handle the tx type ([#182](https://github.com/anton-rs/kona/pull/182)) -- *(ci)* Release plz ([#145](https://github.com/anton-rs/kona/pull/145)) -- *(workspace)* Release plz ([#138](https://github.com/anton-rs/kona/pull/138)) - -### Other -- version dependencies ([#296](https://github.com/anton-rs/kona/pull/296)) -- re-export input types ([#279](https://github.com/anton-rs/kona/pull/279)) -- *(deps)* fast forward op alloy dep ([#267](https://github.com/anton-rs/kona/pull/267)) -- use alloy withdrawal type ([#213](https://github.com/anton-rs/kona/pull/213)) diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml deleted file mode 100644 index 6cb526a52..000000000 --- a/crates/primitives/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "kona-primitives" -description = "Primitive types for kona crates" -version = "0.0.2" -edition.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true -homepage.workspace = true - -[lints] -workspace = true - -[dependencies] -# General -anyhow.workspace = true -thiserror.workspace = true - -# Alloy -alloy-eips.workspace = true -alloy-primitives = { workspace = true, features = ["rlp"] } - -# `serde` feature dependencies -serde = { workspace = true, optional = true } - -# `online` feature dependencies -tracing = { workspace = true, optional = true } -revm = { workspace = true, optional = true } -sha2 = { workspace = true, optional = true } -c-kzg = { workspace = true, optional = true } - -[dev-dependencies] -serde_json.workspace = true - -[features] -default = ["serde"] -serde = ["dep:serde", "alloy-primitives/serde"] -online = [ - "dep:c-kzg", - "dep:sha2", - "dep:revm", - "dep:tracing", - "revm/default", -] diff --git a/crates/primitives/README.md b/crates/primitives/README.md deleted file mode 100644 index ec442b00e..000000000 --- a/crates/primitives/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# `kona-primitives` - -Primitive types for kona crates. - -These types SHOULD be re-exported by downstream kona crates. diff --git a/crates/primitives/src/blob.rs b/crates/primitives/src/blob.rs deleted file mode 100644 index d42290218..000000000 --- a/crates/primitives/src/blob.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! EIP4844 Blob Type - -use alloc::vec; -use alloy_eips::eip4844::{Blob, BYTES_PER_BLOB, VERSIONED_HASH_VERSION_KZG}; -use alloy_primitives::Bytes; -use thiserror::Error; - -/// The blob encoding version -pub(crate) const BLOB_ENCODING_VERSION: u8 = 0; - -/// Maximum blob data size -pub(crate) const BLOB_MAX_DATA_SIZE: usize = (4 * 31 + 3) * 1024 - 4; // 130044 - -/// Blob Encoding/Decoding Rounds -pub(crate) const BLOB_ENCODING_ROUNDS: usize = 1024; - -/// Blob Decuding Error -#[derive(Error, Debug, PartialEq, Eq)] -pub enum BlobDecodingError { - /// Invalid field element - #[error("Invalid field element")] - InvalidFieldElement, - /// Invalid encoding version - #[error("Invalid encoding version")] - InvalidEncodingVersion, - /// Invalid length - #[error("Invalid length")] - InvalidLength, - /// Missing Data - #[error("Missing data")] - MissingData, -} - -/// The Blob Data -#[derive(Default, Clone, Debug)] -pub struct BlobData { - /// The blob data - pub data: Option, - /// The calldata - pub calldata: Option, -} - -impl BlobData { - /// Decodes the blob into raw byte data. - /// Returns a [BlobDecodingError] if the blob is invalid. - pub fn decode(&self) -> Result { - let data = self.data.as_ref().ok_or(BlobDecodingError::MissingData)?; - - // Validate the blob encoding version - if data[VERSIONED_HASH_VERSION_KZG as usize] != BLOB_ENCODING_VERSION { - return Err(BlobDecodingError::InvalidEncodingVersion); - } - - // Decode the 3 byte big endian length value into a 4 byte integer - let length = u32::from_be_bytes([0, data[2], data[3], data[4]]) as usize; - - // Validate the length - if length > BLOB_MAX_DATA_SIZE { - return Err(BlobDecodingError::InvalidLength); - } - - // Round 0 copies the remaining 27 bytes of the first field element - let mut output = vec![0u8; BLOB_MAX_DATA_SIZE]; - output[0..27].copy_from_slice(&data[5..32]); - - // Process the remaining 3 field elements to complete round 0 - let mut output_pos = 28; - let mut input_pos = 32; - let mut encoded_byte = [0u8; 4]; - encoded_byte[0] = data[0]; - - for b in encoded_byte.iter_mut().skip(1) { - let (enc, opos, ipos) = - self.decode_field_element(output_pos, input_pos, &mut output)?; - *b = enc; - output_pos = opos; - input_pos = ipos; - } - - // Reassemble the 4 by 6 bit encoded chunks into 3 bytes of output - output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output); - - // In each remaining round, decode 4 field elements (128 bytes) of the - // input into 127 bytes of output - for _ in 1..BLOB_ENCODING_ROUNDS { - // Break early if the output position is greater than the length - if output_pos >= length { - break; - } - - for d in &mut encoded_byte { - let (enc, opos, ipos) = - self.decode_field_element(output_pos, input_pos, &mut output)?; - *d = enc; - output_pos = opos; - input_pos = ipos; - } - output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output); - } - - // Validate the remaining bytes - for o in output.iter().skip(length) { - if *o != 0u8 { - return Err(BlobDecodingError::InvalidFieldElement); - } - } - - // Validate the remaining bytes - output.truncate(length); - for i in input_pos..BYTES_PER_BLOB { - if data[i] != 0 { - return Err(BlobDecodingError::InvalidFieldElement); - } - } - - Ok(Bytes::from(output)) - } - - /// Decodes the next input field element by writing its lower 31 bytes into its - /// appropriate place in the output and checking the high order byte is valid. - /// Returns a [BlobDecodingError] if a field element is seen with either of its - /// two high order bits set. - pub fn decode_field_element( - &self, - output_pos: usize, - input_pos: usize, - output: &mut [u8], - ) -> Result<(u8, usize, usize), BlobDecodingError> { - let Some(data) = self.data.as_ref() else { - return Err(BlobDecodingError::MissingData); - }; - - // two highest order bits of the first byte of each field element should always be 0 - if data[input_pos] & 0b1100_0000 != 0 { - return Err(BlobDecodingError::InvalidFieldElement); - } - output[output_pos..output_pos + 31].copy_from_slice(&data[input_pos + 1..input_pos + 32]); - Ok((data[input_pos], output_pos + 32, input_pos + 32)) - } - - /// Reassemble 4 by 6 bit encoded chunks into 3 bytes of output and place them in their - /// appropriate output positions. - pub fn reassemble_bytes( - &self, - mut output_pos: usize, - encoded_byte: &[u8], - output: &mut [u8], - ) -> usize { - output_pos -= 1; - let x = (encoded_byte[0] & 0b0011_1111) | ((encoded_byte[1] & 0b0011_0000) << 2); - let y = (encoded_byte[1] & 0b0000_1111) | ((encoded_byte[3] & 0b0000_1111) << 4); - let z = (encoded_byte[2] & 0b0011_1111) | ((encoded_byte[3] & 0b0011_0000) << 2); - output[output_pos - 32] = z; - output[output_pos - (32 * 2)] = y; - output[output_pos - (32 * 3)] = x; - output_pos - } - - /// Fills in the pointers to the fetched blob bodies. - /// There should be exactly one placeholder blobOrCalldata - /// element for each blob, otherwise an error is returned. - pub fn fill(&mut self, blobs: &[Blob], index: usize) -> Result<(), BlobDecodingError> { - // Do not fill if there is no calldata to fill - if self.calldata.as_ref().map_or(false, |data| data.is_empty()) { - return Ok(()); - } - - if index >= blobs.len() { - return Err(BlobDecodingError::InvalidLength); - } - - if blobs[index].is_empty() { - return Err(BlobDecodingError::MissingData); - } - - self.data = Some(Bytes::from(blobs[index])); - Ok(()) - } - - /// Returns if a blob is empty. - pub const fn is_empty(&self) -> bool { - self.data.is_none() && self.calldata.is_none() - } - - /// Turns the blob into its inner data. - pub fn inner(&self) -> anyhow::Result { - if let Some(data) = &self.calldata { - return Ok(data.clone()); - } - if let Some(data) = &self.data { - return Ok(data.clone()); - } - Err(anyhow::anyhow!("No data found")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_empty() { - let blob_data = BlobData::default(); - assert!(blob_data.is_empty()); - } - - #[test] - fn test_reassemble_bytes() { - let blob_data = BlobData::default(); - let mut output = vec![0u8; 128]; - let encoded_byte = [0x00, 0x00, 0x00, 0x00]; - let output_pos = blob_data.reassemble_bytes(127, &encoded_byte, &mut output); - assert_eq!(output_pos, 126); - assert_eq!(output, vec![0u8; 128]); - } - - #[test] - fn test_cannot_fill_empty_calldata() { - let mut blob_data = BlobData { calldata: Some(Bytes::new()), ..Default::default() }; - let blobs = vec![Blob::with_last_byte(1u8)]; - assert_eq!(blob_data.fill(&blobs, 0), Ok(())); - } - - #[test] - fn test_fill_oob_index() { - let mut blob_data = BlobData::default(); - let blobs = vec![Blob::with_last_byte(1u8)]; - assert_eq!(blob_data.fill(&blobs, 1), Err(BlobDecodingError::InvalidLength)); - } - - #[test] - #[ignore] - fn test_fill_empty_blob() { - let mut blob_data = BlobData::default(); - let blobs = vec![Blob::ZERO]; - assert_eq!(blob_data.fill(&blobs, 0), Err(BlobDecodingError::MissingData)); - } - - #[test] - fn test_fill_blob() { - let mut blob_data = BlobData::default(); - let blobs = vec![Blob::with_last_byte(1u8)]; - assert_eq!(blob_data.fill(&blobs, 0), Ok(())); - let expected = Bytes::from([&[0u8; 131071][..], &[1u8]].concat()); - assert_eq!(blob_data.data, Some(expected)); - } - - #[test] - fn test_not_empty() { - let mut blob_data = - BlobData { data: Some(Bytes::from(vec![1u8; 32])), ..Default::default() }; - assert!(!blob_data.is_empty()); - blob_data.data = None; - blob_data.calldata = Some(Bytes::from(vec![1u8; 32])); - assert!(!blob_data.is_empty()); - } - - #[test] - fn test_inner() { - let blob_data = BlobData { data: Some(Bytes::from(vec![1u8; 32])), ..Default::default() }; - assert_eq!(blob_data.inner().unwrap(), Bytes::from(vec![1u8; 32])); - let blob_data = - BlobData { calldata: Some(Bytes::from(vec![1u8; 32])), ..Default::default() }; - assert_eq!(blob_data.inner().unwrap(), Bytes::from(vec![1u8; 32])); - let blob_data = BlobData::default(); - assert!(blob_data.inner().is_err()); - } - - #[test] - fn test_blob_data_decode_missing_data() { - let blob_data = BlobData::default(); - assert_eq!(blob_data.decode(), Err(BlobDecodingError::MissingData)); - } - - #[test] - fn test_blob_data_decode_invalid_encoding_version() { - let blob_data = BlobData { data: Some(Bytes::from(vec![1u8; 32])), ..Default::default() }; - assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidEncodingVersion)); - } - - #[test] - fn test_blob_data_decode_invalid_length() { - let mut data = vec![0u8; 32]; - data[VERSIONED_HASH_VERSION_KZG as usize] = BLOB_ENCODING_VERSION; - data[2] = 0xFF; - data[3] = 0xFF; - data[4] = 0xFF; - let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() }; - assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidLength)); - } -} diff --git a/crates/primitives/src/blob_hash.rs b/crates/primitives/src/blob_hash.rs deleted file mode 100644 index dd51b630d..000000000 --- a/crates/primitives/src/blob_hash.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Indexed Blob Hash. - -use alloy_primitives::B256; - -/// A Blob hash -#[derive(Default, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct IndexedBlobHash { - /// The index of the blob - pub index: usize, - /// The hash of the blob - pub hash: B256, -} - -impl PartialEq for IndexedBlobHash { - fn eq(&self, other: &Self) -> bool { - self.index == other.index && self.hash == other.hash - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_indexed_blob_hash() { - let hash = B256::from([1; 32]); - let indexed_blob_hash = IndexedBlobHash { index: 1, hash }; - - assert_eq!(indexed_blob_hash.index, 1); - assert_eq!(indexed_blob_hash.hash, hash); - } - - #[test] - #[cfg(feature = "serde")] - fn test_indexed_blob_hash_serde_roundtrip() { - let hash = B256::from([1; 32]); - let indexed_blob_hash = IndexedBlobHash { index: 1, hash }; - - let serialized = serde_json::to_string(&indexed_blob_hash).unwrap(); - let deserialized: IndexedBlobHash = serde_json::from_str(&serialized).unwrap(); - - assert_eq!(indexed_blob_hash, deserialized); - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs deleted file mode 100644 index 6e4e3ec72..000000000 --- a/crates/primitives/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![doc = include_str!("../README.md")] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/anton-rs/kona/main/assets/square.png", - html_favicon_url = "https://raw.githubusercontent.com/anton-rs/kona/main/assets/favicon.ico" -)] -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(test), no_std)] - -extern crate alloc; - -pub mod blob_hash; -pub use blob_hash::IndexedBlobHash; - -pub mod blob; -pub use blob::{BlobData, BlobDecodingError}; - -pub mod sidecar; -pub use sidecar::{ - APIBlobSidecar, APIConfigResponse, APIGenesisResponse, APIGetBlobSidecarsResponse, - APIVersionResponse, BeaconBlockHeader, BlobSidecar, SignedBeaconBlockHeader, - VersionInformation, KZG_COMMITMENT_SIZE, KZG_PROOF_SIZE, -}; diff --git a/crates/primitives/src/sidecar.rs b/crates/primitives/src/sidecar.rs deleted file mode 100644 index 80682da6b..000000000 --- a/crates/primitives/src/sidecar.rs +++ /dev/null @@ -1,247 +0,0 @@ -//! Contains sidecar types for blobs. - -use alloc::{string::String, vec::Vec}; -use alloy_eips::eip4844::Blob; -use alloy_primitives::FixedBytes; - -#[cfg(feature = "online")] -use alloy_eips::eip4844::VERSIONED_HASH_VERSION_KZG; - -#[cfg(feature = "online")] -use crate::IndexedBlobHash; -#[cfg(feature = "online")] -use alloy_primitives::B256; -#[cfg(feature = "online")] -use c_kzg::{Bytes48, KzgProof}; -#[cfg(feature = "online")] -use revm::primitives::EnvKzgSettings; -#[cfg(feature = "online")] -use sha2::{Digest, Sha256}; -#[cfg(feature = "online")] -use tracing::warn; - -#[cfg(feature = "serde")] -use serde::de::Deserialize; - -#[cfg(feature = "serde")] -use core::str::FromStr; - -/// KZG Proof Size -pub const KZG_PROOF_SIZE: usize = 48; - -/// KZG Commitment Size -pub const KZG_COMMITMENT_SIZE: usize = 48; - -#[cfg(feature = "serde")] -fn parse_u64_string<'de, T, D>(de: D) -> Result -where - D: serde::Deserializer<'de>, - T: FromStr, - ::Err: core::fmt::Display, -{ - String::deserialize(de)?.parse().map_err(serde::de::Error::custom) -} - -/// A blob sidecar. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlobSidecar { - /// The blob. - pub blob: Blob, - /// The index. - #[cfg_attr(feature = "serde", serde(deserialize_with = "parse_u64_string"))] - pub index: u64, - /// The KZG commitment. - #[cfg_attr(feature = "serde", serde(rename = "kzg_commitment"))] - pub kzg_commitment: FixedBytes, - /// The KZG proof. - #[cfg_attr(feature = "serde", serde(rename = "kzg_proof"))] - pub kzg_proof: FixedBytes, -} - -impl BlobSidecar { - /// Verify the blob sidecar against it's [IndexedBlobHash]. - #[cfg(feature = "online")] - pub fn verify_blob(&self, hash: &IndexedBlobHash) -> anyhow::Result<()> { - if self.index as usize != hash.index { - return Err(anyhow::anyhow!( - "invalid sidecar ordering, blob hash index {} does not match sidecar index {}", - hash.index, - self.index - )); - } - - // Ensure the blob's kzg commitment hashes to the expected value. - if self.to_kzg_versioned_hash() != hash.hash { - return Err(anyhow::anyhow!( - "expected hash {} for blob at index {} but got {}", - hash.hash, - hash.index, - B256::from(self.to_kzg_versioned_hash()) - )); - } - - // Confirm blob data is valid by verifying its proof against the commitment - match self.verify_blob_kzg_proof() { - Ok(true) => Ok(()), - Ok(false) => Err(anyhow::anyhow!("blob at index {} failed verification", self.index)), - Err(e) => { - Err(anyhow::anyhow!("blob at index {} failed verification: {}", self.index, e)) - } - } - } - - /// Verifies the blob kzg proof. - #[cfg(feature = "online")] - pub fn verify_blob_kzg_proof(&self) -> anyhow::Result { - let how = |e: c_kzg::Error| anyhow::anyhow!(e); - let blob = c_kzg::Blob::from_bytes(self.blob.as_slice()).map_err(how)?; - let commitment = Bytes48::from_bytes(self.kzg_commitment.as_slice()).map_err(how)?; - let kzg_proof = Bytes48::from_bytes(self.kzg_proof.as_slice()).map_err(how)?; - let settings = EnvKzgSettings::Default.get(); - match KzgProof::verify_blob_kzg_proof(&blob, &commitment, &kzg_proof, settings) { - Ok(_) => Ok(true), - Err(e) => { - warn!("Failed to verify blob KZG proof: {:?}", e); - Ok(false) - } - } - } - - /// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` - #[cfg(feature = "online")] - pub fn to_kzg_versioned_hash(&self) -> [u8; 32] { - let commitment = self.kzg_commitment.as_slice(); - let mut hash: [u8; 32] = Sha256::digest(commitment).into(); - hash[0] = VERSIONED_HASH_VERSION_KZG; - hash - } -} - -/// An API blob sidecar. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct APIBlobSidecar { - /// The inner blob sidecar. - #[cfg_attr(feature = "serde", serde(flatten))] - pub inner: BlobSidecar, - /// The signed block header. - #[cfg_attr(feature = "serde", serde(rename = "signed_block_header"))] - pub signed_block_header: SignedBeaconBlockHeader, - // The inclusion-proof of the blob-sidecar into the beacon-block is ignored, - // since we verify blobs by their versioned hashes against the execution-layer block instead. -} - -/// A signed beacon block header. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SignedBeaconBlockHeader { - /// The message. - pub message: BeaconBlockHeader, - // The signature is ignored, since we verify blobs against EL versioned-hashes -} - -/// A beacon block header. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BeaconBlockHeader { - /// The slot. - #[cfg_attr(feature = "serde", serde(deserialize_with = "parse_u64_string"))] - pub slot: u64, - /// The proposer index. - #[cfg_attr(feature = "serde", serde(deserialize_with = "parse_u64_string"))] - pub proposer_index: u64, - /// The parent root. - #[cfg_attr(feature = "serde", serde(rename = "parent_root"))] - pub parent_root: FixedBytes<32>, - /// The state root. - #[cfg_attr(feature = "serde", serde(rename = "state_root"))] - pub state_root: FixedBytes<32>, - /// The body root. - #[cfg_attr(feature = "serde", serde(rename = "body_root"))] - pub body_root: FixedBytes<32>, -} - -/// An response for fetching blob sidecars. -#[derive(Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct APIGetBlobSidecarsResponse { - /// The data. - pub data: Vec, -} - -impl Clone for APIGetBlobSidecarsResponse { - fn clone(&self) -> Self { - let mut data = Vec::with_capacity(self.data.len()); - for sidecar in &self.data { - data.push(sidecar.clone()); - } - Self { data } - } -} - -/// A reduced genesis data. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ReducedGenesisData { - /// The genesis time. - #[cfg_attr(feature = "serde", serde(rename = "genesis_time"))] - #[cfg_attr(feature = "serde", serde(deserialize_with = "parse_u64_string"))] - pub genesis_time: u64, -} - -/// An API genesis response. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct APIGenesisResponse { - /// The data. - pub data: ReducedGenesisData, -} - -impl APIGenesisResponse { - /// Creates a new API genesis response. - pub const fn new(genesis_time: u64) -> Self { - Self { data: ReducedGenesisData { genesis_time } } - } -} - -/// A reduced config data. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ReducedConfigData { - /// The seconds per slot. - #[cfg_attr(feature = "serde", serde(rename = "SECONDS_PER_SLOT"))] - #[cfg_attr(feature = "serde", serde(deserialize_with = "parse_u64_string"))] - pub seconds_per_slot: u64, -} - -/// An API config response. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct APIConfigResponse { - /// The data. - pub data: ReducedConfigData, -} - -impl APIConfigResponse { - /// Creates a new API config response. - pub const fn new(seconds_per_slot: u64) -> Self { - Self { data: ReducedConfigData { seconds_per_slot } } - } -} - -/// An API version response. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct APIVersionResponse { - /// The data. - pub data: VersionInformation, -} - -/// Version information. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct VersionInformation { - /// The version. - pub version: String, -} diff --git a/crates/providers-alloy/Cargo.toml b/crates/providers-alloy/Cargo.toml index e1818a50c..ca8aca751 100644 --- a/crates/providers-alloy/Cargo.toml +++ b/crates/providers-alloy/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [dependencies] # Alloy -alloy-eips.workspace = true +alloy-eips = { workspace = true, features = ["kzg"] } alloy-transport.workspace = true alloy-provider = { workspace = true, features = ["reqwest"] } alloy-rlp = { workspace = true, features = ["derive"] } @@ -27,6 +27,8 @@ op-alloy-genesis = { workspace = true, features = ["serde", "std"] } # General lru.workspace = true +serde.workspace = true +alloy-serde.workspace = true reqwest.workspace = true tracing.workspace = true async-trait.workspace = true @@ -34,7 +36,6 @@ async-trait.workspace = true # Workspace kona-providers.workspace = true kona-derive = { workspace = true, features = ["serde"] } -kona-primitives = { workspace = true, features = ["serde", "online"] } # `metrics` feature dependencies lazy_static = { workspace = true, optional = true } diff --git a/crates/providers-alloy/src/beacon_client.rs b/crates/providers-alloy/src/beacon_client.rs index f3bd23f7e..658e5b135 100644 --- a/crates/providers-alloy/src/beacon_client.rs +++ b/crates/providers-alloy/src/beacon_client.rs @@ -1,10 +1,8 @@ //! Contains an online implementation of the `BeaconClient` trait. +use alloy_eips::{eip1898::NumHash, eip4844::BlobTransactionSidecarItem}; +use alloy_primitives::FixedBytes; use async_trait::async_trait; -use kona_primitives::{ - APIBlobSidecar, APIConfigResponse, APIGenesisResponse, APIGetBlobSidecarsResponse, - IndexedBlobHash, -}; use reqwest::Client; /// The config spec engine api method. @@ -16,6 +14,124 @@ pub(crate) const GENESIS_METHOD: &str = "eth/v1/beacon/genesis"; /// The blob sidecars engine api method prefix. pub(crate) const SIDECARS_METHOD_PREFIX: &str = "eth/v1/beacon/blob_sidecars"; +/// An API blob sidecar. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct APIBlobSidecar { + /// The inner blob sidecar. + #[serde(flatten)] + pub inner: BlobTransactionSidecarItem, + /// The signed block header. + #[serde(rename = "signed_block_header")] + pub signed_block_header: SignedBeaconBlockHeader, + // The inclusion-proof of the blob-sidecar into the beacon-block is ignored, + // since we verify blobs by their versioned hashes against the execution-layer block instead. +} + +/// A signed beacon block header. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SignedBeaconBlockHeader { + /// The message. + pub message: BeaconBlockHeader, + // The signature is ignored, since we verify blobs against EL versioned-hashes +} + +/// A beacon block header. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BeaconBlockHeader { + /// The slot. + #[serde(with = "alloy_serde::quantity")] + pub slot: u64, + /// The proposer index. + #[serde(with = "alloy_serde::quantity")] + pub proposer_index: u64, + /// The parent root. + #[serde(rename = "parent_root")] + pub parent_root: FixedBytes<32>, + /// The state root. + #[serde(rename = "state_root")] + pub state_root: FixedBytes<32>, + /// The body root. + #[serde(rename = "body_root")] + pub body_root: FixedBytes<32>, +} + +/// An response for fetching blob sidecars. +#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] +pub struct APIGetBlobSidecarsResponse { + /// The data. + pub data: Vec, +} + +/// A reduced genesis data. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ReducedGenesisData { + /// The genesis time. + #[serde(rename = "genesis_time")] + #[serde(with = "alloy_serde::quantity")] + pub genesis_time: u64, +} + +/// An API genesis response. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct APIGenesisResponse { + /// The data. + pub data: ReducedGenesisData, +} + +/// A reduced config data. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ReducedConfigData { + /// The seconds per slot. + #[serde(rename = "SECONDS_PER_SLOT")] + #[serde(with = "alloy_serde::quantity")] + pub seconds_per_slot: u64, +} + +/// An API config response. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct APIConfigResponse { + /// The data. + pub data: ReducedConfigData, +} + +impl APIConfigResponse { + /// Creates a new API config response. + pub const fn new(seconds_per_slot: u64) -> Self { + Self { data: ReducedConfigData { seconds_per_slot } } + } +} + +/// An API version response. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct APIVersionResponse { + /// The data. + pub data: VersionInformation, +} + +/// Version information. +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct VersionInformation { + /// The version. + pub version: String, +} + +impl APIGenesisResponse { + /// Creates a new API genesis response. + pub const fn new(genesis_time: u64) -> Self { + Self { data: ReducedGenesisData { genesis_time } } + } +} + +impl Clone for APIGetBlobSidecarsResponse { + fn clone(&self) -> Self { + let mut data = Vec::with_capacity(self.data.len()); + for sidecar in &self.data { + data.push(sidecar.clone()); + } + Self { data } + } +} + /// The [BeaconClient] is a thin wrapper around the Beacon API. #[async_trait] pub trait BeaconClient { @@ -34,7 +150,7 @@ pub trait BeaconClient { async fn beacon_blob_side_cars( &self, slot: u64, - hashes: &[IndexedBlobHash], + hashes: &[NumHash], ) -> Result, Self::Error>; } @@ -103,7 +219,7 @@ impl BeaconClient for OnlineBeaconClient { async fn beacon_blob_side_cars( &self, slot: u64, - hashes: &[IndexedBlobHash], + hashes: &[NumHash], ) -> Result, Self::Error> { crate::inc!(PROVIDER_CALLS, &["beacon_client", "beacon_blob_side_cars"]); crate::timer!( @@ -142,7 +258,7 @@ impl BeaconClient for OnlineBeaconClient { // Filter the sidecars by the hashes, in-order. hashes.iter().for_each(|hash| { if let Some(sidecar) = - raw_response.data.iter().find(|sidecar| sidecar.inner.index == hash.index as u64) + raw_response.data.iter().find(|sidecar| sidecar.inner.index == hash.number) { sidecars.push(sidecar.clone()); } diff --git a/crates/providers-alloy/src/blob_provider.rs b/crates/providers-alloy/src/blob_provider.rs index 56a9d90c2..5f6d1edc3 100644 --- a/crates/providers-alloy/src/blob_provider.rs +++ b/crates/providers-alloy/src/blob_provider.rs @@ -1,13 +1,15 @@ //! Contains an online implementation of the `BlobProvider` trait. -use alloy_eips::eip4844::Blob; +use alloy_eips::{ + eip1898::NumHash, + eip4844::{Blob, BlobTransactionSidecarItem}, +}; use async_trait::async_trait; use kona_derive::{errors::BlobProviderError, traits::BlobProvider}; -use kona_primitives::{APIBlobSidecar, BlobSidecar, IndexedBlobHash}; use op_alloy_protocol::BlockInfo; use tracing::warn; -use crate::{BeaconClient, OnlineBeaconClient}; +use crate::{APIBlobSidecar, BeaconClient, OnlineBeaconClient}; /// An online implementation of the [BlobProvider] trait. #[derive(Debug, Clone)] @@ -63,7 +65,7 @@ impl OnlineBlobProvider { pub async fn fetch_sidecars( &self, slot: u64, - hashes: &[IndexedBlobHash], + hashes: &[NumHash], ) -> Result, BlobProviderError> { self.beacon_client .beacon_blob_side_cars(slot, hashes) @@ -81,8 +83,8 @@ impl OnlineBlobProvider { pub async fn fetch_filtered_sidecars( &self, block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, BlobProviderError> { + blob_hashes: &[NumHash], + ) -> Result, BlobProviderError> { if blob_hashes.is_empty() { return Ok(Vec::new()); } @@ -98,10 +100,10 @@ impl OnlineBlobProvider { let sidecars = self.fetch_sidecars(slot, blob_hashes).await?; // Filter blob sidecars that match the indicies in the specified list. - let blob_hash_indicies = blob_hashes.iter().map(|b| b.index).collect::>(); + let blob_hash_indicies = blob_hashes.iter().map(|b| b.number).collect::>(); let filtered = sidecars .into_iter() - .filter(|s| blob_hash_indicies.contains(&(s.inner.index as usize))) + .filter(|s| blob_hash_indicies.contains(&s.inner.index)) .collect::>(); // Validate the correct number of blob sidecars were retrieved. @@ -109,7 +111,7 @@ impl OnlineBlobProvider { return Err(BlobProviderError::SidecarLengthMismatch(blob_hashes.len(), filtered.len())); } - Ok(filtered.into_iter().map(|s| s.inner).collect::>()) + Ok(filtered.into_iter().map(|s| s.inner).collect::>()) } } @@ -122,12 +124,12 @@ where /// Fetches blob sidecars that were confirmed in the specified L1 block with the given indexed /// hashes. The blobs are validated for their index and hashes using the specified - /// [IndexedBlobHash]. + /// [NumHash]. async fn get_blobs( &mut self, block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, Self::Error> { + blob_hashes: &[NumHash], + ) -> Result>, Self::Error> { crate::inc!(PROVIDER_CALLS, &["blob_provider", "get_blobs"]); crate::timer!(START, PROVIDER_RESPONSE_TIME, &["blob_provider", "get_blobs"], timer); // Fetches the genesis timestamp and slot interval from the @@ -151,7 +153,7 @@ where } }; - // Validate the blob sidecars straight away with the `IndexedBlobHash`es. + // Validate the blob sidecars straight away with the num hashes. let blobs = match sidecars .into_iter() .enumerate() @@ -164,7 +166,7 @@ where Err(e) => Err(BlobProviderError::Backend(e.to_string())), } }) - .collect::, BlobProviderError>>() + .collect::>, BlobProviderError>>() { Ok(blobs) => blobs, Err(e) => { @@ -189,7 +191,7 @@ pub trait BlobSidecarProvider { async fn beacon_blob_side_cars( &self, slot: u64, - hashes: &[IndexedBlobHash], + hashes: &[NumHash], ) -> Result, BlobProviderError>; } @@ -200,7 +202,7 @@ impl BlobSidecarProvider for B { async fn beacon_blob_side_cars( &self, slot: u64, - hashes: &[IndexedBlobHash], + hashes: &[NumHash], ) -> Result, BlobProviderError> { self.beacon_blob_side_cars(slot, hashes) .await @@ -235,8 +237,8 @@ impl OnlineBlobProviderWithFallback Result, BlobProviderError> { + blob_hashes: &[NumHash], + ) -> Result, BlobProviderError> { let Some(fallback) = self.fallback.as_ref() else { return Err(BlobProviderError::Backend( "cannot fetch blobs: the primary blob provider failed, and no fallback is configured".to_string() @@ -258,10 +260,10 @@ impl OnlineBlobProviderWithFallback>(); + let blob_hash_indicies = blob_hashes.iter().map(|b| b.number).collect::>(); let filtered = sidecars .into_iter() - .filter(|s| blob_hash_indicies.contains(&(s.inner.index as usize))) + .filter(|s| blob_hash_indicies.contains(&s.inner.index)) .collect::>(); // Validate the correct number of blob sidecars were retrieved. @@ -269,7 +271,7 @@ impl OnlineBlobProviderWithFallback>()) + Ok(filtered.into_iter().map(|s| s.inner).collect::>()) } } @@ -283,12 +285,12 @@ where /// Fetches blob sidecars that were confirmed in the specified L1 block with the given indexed /// hashes. The blobs are validated for their index and hashes using the specified - /// [IndexedBlobHash]. + /// [NumHash]. async fn get_blobs( &mut self, block_ref: &BlockInfo, - blob_hashes: &[IndexedBlobHash], - ) -> Result, BlobProviderError> { + blob_hashes: &[NumHash], + ) -> Result>, BlobProviderError> { match self.primary.get_blobs(block_ref, blob_hashes).await { Ok(blobs) => Ok(blobs), Err(primary_err) => { @@ -309,7 +311,7 @@ where } }; - // Validate the blob sidecars straight away with the `IndexedBlobHash`es. + // Validate the blob sidecars straight away with the num hashes. let blobs = match sidecars .into_iter() .enumerate() @@ -322,7 +324,7 @@ where Err(e) => Err(BlobProviderError::Backend(e.to_string())), } }) - .collect::, BlobProviderError>>() + .collect::>, BlobProviderError>>() { Ok(blobs) => blobs, Err(e) => { @@ -435,9 +437,11 @@ impl From Result, Self::Error> { self.blob_sidecars .clone()