diff --git a/Cargo.lock b/Cargo.lock index 478cba998f..f59b9a6ae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a70d19a83dfee0cd4b16d28d2fc1c822a9a55935c672259dd8165e342c4147" +checksum = "1468e3128e07c7afe4ff13c17e8170c330d12c322f8924b8bf6986a27e0aad3d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecd43a1ce87109f4d64efeff2b6fd0d7ff06afe93c4ffeb4e23bbb34d77ce84" +checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -321,9 +321,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d8c017799918fe4c742fcd3d19a7c7b5839aea818f02e15e1bb37292b8a513" +checksum = "0b7210f9206c0fa2a83c824cf8cb6c962126bc9fdc4f41ade1932f14150ef5f6" dependencies = [ "alloy-primitives", "alloy-serde", @@ -332,9 +332,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92fdc402c955dc61b415325c1ff36988bd1d2287146b8123f0f997013343a118" +checksum = "8866562186d237f1dfeaf989ef941a24764f764bf5c33311e37ead3519c6a429" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -346,9 +346,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da63445b84f4cd2c5b5b62b7ebf57125e4b9ce233d45db4d2b4a0f329f5fe5a" +checksum = "abe714e233f9eaf410de95a9af6bcd05d3a7f8c8de7a0817221e95a6b642a080" dependencies = [ "alloy-consensus", "alloy-eips", @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb528a622b49b51c79d4ba61e3ff2567b31bd339571e8e06e36b94494cc49a" +checksum = "8c5a38117974c5776a45e140226745a0b664f79736aa900995d8e4121558e064" dependencies = [ "alloy-eips", "alloy-primitives", @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1a1bf5cd6ec144b38628403565d14b0382d0f543a818e6a45824496ca14a3c" +checksum = "d149d4f3147b3494e1b1db8704e9fdb579e8c666c3deb7d070ebd5f38c2abb15" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -395,9 +395,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb865df835f851b367ae439d6c82b117ded971628c8888b24fed411a290e38a" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" dependencies = [ "alloy-rlp", "bytes", @@ -417,9 +417,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b624bfbfa145b64571650ce951eba3ec1d38b781e4fe2b5a8e7410f99f73336" +checksum = "c65633d6ef83c3626913c004eaf166a6dd50406f724772ea8567135efd6dc5d3" dependencies = [ "alloy-chains", "alloy-consensus", @@ -473,9 +473,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02af2846b186e4ae25eb448dd78aebdb82f55668bdc360a3a7bf4c7754cb8251" +checksum = "d5fc328bb5d440599ba1b5aa44c0b9ab0625fbc3a403bb5ee94ed4a01ba23e07" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f1407e7da4a25fef7d4e6214e887b0b084e8ff02e2c26506ad55c0aa972ca10" +checksum = "8f8ff679f94c497a8383f2cd09e2a099266e5f3d5e574bc82b4b379865707dbb" dependencies = [ "alloy-rpc-types-eth", "alloy-serde", @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cc832ec2ad270ac9859cc5056117890af943686d432a2c478eafef5102e8eb" +checksum = "9a59b1d7c86e0a653e7f3d29954f6de5a2878d8cfd1f010ff93be5c2c48cd3b1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -524,9 +524,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c4dbeff85bba7bbacb5df2a05e68e426aed994669785017a9d33b4cee32f4" +checksum = "51db8a6428a2159e01b7a43ec7aac801edd0c4db1d4de06f310c288940f16fd3" dependencies = [ "alloy-primitives", "serde", @@ -535,9 +535,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d458fc2eec44beb3c64cb987941e992177390ecd47b291eeb3d1ec264b5f70ff" +checksum = "bebc1760c13592b7ba3fcd964abba546b8d6a9f10d15e8d92a8263731be33f36" dependencies = [ "alloy-primitives", "async-trait", @@ -549,9 +549,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2dc5201ca0018afb7a3e0cd8bd15f7ca6aca924333b5f3bb87463b41d0c4ef2" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -563,9 +563,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155f63dc6945885aa4532601800201fddfaa3b20901fda8e8c2570327242fe0e" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -581,9 +581,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847700aa9cb59d3c7b290b2d05976cd8d76b64d73bb63116a9533132d995586b" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" dependencies = [ "const-hex", "dunce", @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83665e5607725a7a1aab3cb0dea708f4a05e70776954ec7f0a9461439175c957" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98b7bf2a9e0671a1e914e89854e74c89e62a549da548184c6d93c2be828a575" +checksum = "fd5dc4e902f1860d54952446d246ac05386311ad61030a2b906ae865416d36e0" dependencies = [ "alloy-json-rpc", "base64", @@ -626,9 +626,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85d928f36065b0f548782ec2acf4b54a5cf8b297a22ca18d2c8fe165af90561f" +checksum = "1742b94bb814f1ca6b322a6f9dd38a0252ff45a3119e40e888fb7029afa500ce" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -711,9 +711,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" [[package]] name = "ark-ff" @@ -2255,9 +2255,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -3731,9 +3731,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -3744,9 +3744,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "once_cell", "rustls-pki-types", @@ -3996,9 +3996,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if", @@ -4255,9 +4255,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1355d44af21638c8e05d45097db6cb5ec2aa3e970c51cb2901605cf3344fa" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" dependencies = [ "paste", "proc-macro2", @@ -4725,9 +4725,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" diff --git a/crates/primitives/src/deposits.rs b/crates/primitives/src/deposits.rs deleted file mode 100644 index 431a87239b..0000000000 --- a/crates/primitives/src/deposits.rs +++ /dev/null @@ -1,687 +0,0 @@ -//! Contains deposit transaction types and helper methods. - -use alloc::{string::String, vec::Vec}; -use alloy_eips::eip2718::Encodable2718; -use alloy_primitives::{b256, keccak256, Address, Bytes, Log, TxKind, B256, U256, U64}; -use alloy_rlp::Encodable; -use core::fmt::Display; -use op_alloy_consensus::{OpTxEnvelope, TxDeposit}; - -/// Deposit log event abi signature. -pub const DEPOSIT_EVENT_ABI: &str = "TransactionDeposited(address,address,uint256,bytes)"; - -/// Deposit event abi hash. -/// -/// This is the keccak256 hash of the deposit event ABI signature. -/// `keccak256("TransactionDeposited(address,address,uint256,bytes)")` -pub const DEPOSIT_EVENT_ABI_HASH: B256 = - b256!("b3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32"); - -/// The initial version of the deposit event log. -pub const DEPOSIT_EVENT_VERSION_0: B256 = B256::ZERO; - -/// An [op_alloy_consensus::TxDeposit] validation error. -#[derive(Debug)] -pub enum DepositError { - /// Unexpected number of deposit event log topics. - UnexpectedTopicsLen(usize), - /// Invalid deposit event selector. - /// Expected: [B256] (deposit event selector), Actual: [B256] (event log topic). - InvalidSelector(B256, B256), - /// Incomplete opaqueData slice header (incomplete length). - IncompleteOpaqueData(usize), - /// The log data is not aligned to 32 bytes. - UnalignedData(usize), - /// Failed to decode the `from` field of the deposit event (the second topic). - FromDecode(B256), - /// Failed to decode the `to` field of the deposit event (the third topic). - ToDecode(B256), - /// Invalid opaque data content offset. - InvalidOpaqueDataOffset(Bytes), - /// Invalid opaque data content length. - InvalidOpaqueDataLength(Bytes), - /// Opaque data length exceeds the deposit log event data length. - /// Specified: [usize] (data length), Actual: [usize] (opaque data length). - OpaqueDataOverflow(usize, usize), - /// Opaque data with padding exceeds the specified data length. - PaddedOpaqueDataOverflow(usize, usize), - /// An invalid deposit version. - InvalidVersion(B256), - /// Unexpected opaque data length - UnexpectedOpaqueDataLen(usize), - /// Failed to decode the deposit mint value. - MintDecode(Bytes), - /// Failed to decode the deposit gas value. - GasDecode(Bytes), - /// A custom error wrapping [anyhow::Error]. - Custom(anyhow::Error), -} - -impl PartialEq for DepositError { - fn eq(&self, other: &DepositError) -> bool { - match (self, other) { - (DepositError::UnexpectedTopicsLen(l1), DepositError::UnexpectedTopicsLen(l2)) => { - l1 == l2 - } - (DepositError::InvalidSelector(e1, t1), DepositError::InvalidSelector(e2, t2)) => { - e1 == e2 && t1 == t2 - } - (DepositError::IncompleteOpaqueData(l1), DepositError::IncompleteOpaqueData(l2)) => { - l1 == l2 - } - (DepositError::UnalignedData(d1), DepositError::UnalignedData(d2)) => d1 == d2, - (DepositError::FromDecode(e1), DepositError::FromDecode(e2)) => e1 == e2, - (DepositError::ToDecode(e1), DepositError::ToDecode(e2)) => e1 == e2, - ( - DepositError::InvalidOpaqueDataOffset(o1), - DepositError::InvalidOpaqueDataOffset(o2), - ) => o1 == o2, - ( - DepositError::InvalidOpaqueDataLength(o1), - DepositError::InvalidOpaqueDataLength(o2), - ) => o1 == o2, - ( - DepositError::OpaqueDataOverflow(l1, l2), - DepositError::OpaqueDataOverflow(l3, l4), - ) => l1 == l3 && l2 == l4, - ( - DepositError::PaddedOpaqueDataOverflow(l1, l2), - DepositError::PaddedOpaqueDataOverflow(l3, l4), - ) => l1 == l3 && l2 == l4, - (DepositError::InvalidVersion(v1), DepositError::InvalidVersion(v2)) => v1 == v2, - ( - DepositError::UnexpectedOpaqueDataLen(a), - DepositError::UnexpectedOpaqueDataLen(b), - ) => a == b, - (DepositError::MintDecode(a), DepositError::MintDecode(b)) => a == b, - (DepositError::GasDecode(a), DepositError::GasDecode(b)) => a == b, - (DepositError::Custom(_), DepositError::Custom(_)) => true, - _ => false, - } - } -} - -impl Display for DepositError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - DepositError::UnexpectedTopicsLen(len) => { - write!(f, "Unexpected number of deposit event log topics: {}", len) - } - DepositError::InvalidSelector(expected, actual) => { - write!(f, "Invalid deposit event selector: {}, expected {}", actual, expected) - } - DepositError::IncompleteOpaqueData(len) => { - write!(f, "Incomplete opaqueData slice header (incomplete length): {}", len) - } - DepositError::UnalignedData(data) => { - write!(f, "Unaligned log data, expected multiple of 32 bytes, got: {}", data) - } - DepositError::FromDecode(topic) => { - write!(f, "Failed to decode the `from` address of the deposit log topic: {}", topic) - } - DepositError::ToDecode(topic) => { - write!(f, "Failed to decode the `to` address of the deposit log topic: {}", topic) - } - DepositError::InvalidOpaqueDataOffset(offset) => { - write!(f, "Invalid u64 opaque data content offset: {:?}", offset) - } - DepositError::InvalidOpaqueDataLength(length) => { - write!(f, "Invalid u64 opaque data content length: {:?}", length) - } - DepositError::OpaqueDataOverflow(data_len, opaque_len) => { - write!( - f, - "Specified opaque data length {} exceeds the deposit log event data length {}", - opaque_len, data_len - ) - } - DepositError::PaddedOpaqueDataOverflow(data_len, opaque_len) => { - write!( - f, - "Opaque data with padding exceeds the specified data length: {} > {}", - opaque_len, data_len - ) - } - DepositError::InvalidVersion(version) => { - write!(f, "Invalid deposit version: {}", version) - } - DepositError::UnexpectedOpaqueDataLen(len) => { - write!(f, "Unexpected opaque data length: {}", len) - } - DepositError::MintDecode(data) => { - write!(f, "Failed to decode the u128 deposit mint value: {:?}", data) - } - DepositError::GasDecode(data) => { - write!(f, "Failed to decode the u64 deposit gas value: {:?}", data) - } - DepositError::Custom(e) => write!(f, "Custom error: {}", e), - } - } -} - -/// Source domain identifiers for deposit transactions. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum DepositSourceDomainIdentifier { - /// A user deposit source. - User = 0, - /// A L1 info deposit source. - L1Info = 1, - /// An upgrade deposit source. - Upgrade = 2, -} - -/// Source domains for deposit transactions. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum DepositSourceDomain { - /// A user deposit source. - User(UserDepositSource), - /// A L1 info deposit source. - L1Info(L1InfoDepositSource), - /// An upgrade deposit source. - Upgrade(UpgradeDepositSource), -} - -impl DepositSourceDomain { - /// Returns the source hash. - pub fn source_hash(&self) -> B256 { - match self { - Self::User(ds) => ds.source_hash(), - Self::L1Info(ds) => ds.source_hash(), - Self::Upgrade(ds) => ds.source_hash(), - } - } -} - -/// A deposit transaction source. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UserDepositSource { - /// The L1 block hash. - pub l1_block_hash: B256, - /// The log index. - pub log_index: u64, -} - -impl UserDepositSource { - /// Creates a new [UserDepositSource]. - pub fn new(l1_block_hash: B256, log_index: u64) -> Self { - Self { l1_block_hash, log_index } - } - - /// Returns the source hash. - pub fn source_hash(&self) -> B256 { - let mut input = [0u8; 32 * 2]; - input[..32].copy_from_slice(&self.l1_block_hash[..]); - input[32 * 2 - 8..].copy_from_slice(&self.log_index.to_be_bytes()); - let deposit_id_hash = keccak256(input); - let mut domain_input = [0u8; 32 * 2]; - let identifier_bytes: [u8; 8] = (DepositSourceDomainIdentifier::User as u64).to_be_bytes(); - domain_input[32 - 8..32].copy_from_slice(&identifier_bytes); - domain_input[32..].copy_from_slice(&deposit_id_hash[..]); - keccak256(domain_input) - } -} - -/// A L1 info deposit transaction source. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct L1InfoDepositSource { - /// The L1 block hash. - pub l1_block_hash: B256, - /// The sequence number. - pub seq_number: u64, -} - -impl L1InfoDepositSource { - /// Creates a new [L1InfoDepositSource]. - pub fn new(l1_block_hash: B256, seq_number: u64) -> Self { - Self { l1_block_hash, seq_number } - } - - /// Returns the source hash. - pub fn source_hash(&self) -> B256 { - let mut input = [0u8; 32 * 2]; - input[..32].copy_from_slice(&self.l1_block_hash[..]); - input[32 * 2 - 8..].copy_from_slice(&self.seq_number.to_be_bytes()); - let deposit_id_hash = keccak256(input); - let mut domain_input = [0u8; 32 * 2]; - let identifier_bytes: [u8; 8] = - (DepositSourceDomainIdentifier::L1Info as u64).to_be_bytes(); - domain_input[32 - 8..32].copy_from_slice(&identifier_bytes); - domain_input[32..].copy_from_slice(&deposit_id_hash[..]); - keccak256(domain_input) - } -} - -/// An upgrade deposit transaction source. -/// -/// This implements the translation of upgrade-tx identity information to a deposit source-hash, -/// which makes the deposit uniquely identifiable. -/// System-upgrade transactions have their own domain for source-hashes, -/// to not conflict with user-deposits or deposited L1 information. -/// The intent identifies the upgrade-tx uniquely, in a human-readable way. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UpgradeDepositSource { - /// The intent. - pub intent: String, -} - -impl UpgradeDepositSource { - /// Creates a new [UpgradeDepositSource]. - pub fn new(intent: String) -> Self { - Self { intent } - } - - /// Returns the source hash. - pub fn source_hash(&self) -> B256 { - let intent_hash = keccak256(self.intent.as_bytes()); - let mut domain_input = [0u8; 32 * 2]; - let identifier_bytes: [u8; 8] = - (DepositSourceDomainIdentifier::Upgrade as u64).to_be_bytes(); - domain_input[32 - 8..32].copy_from_slice(&identifier_bytes); - domain_input[32..].copy_from_slice(&intent_hash[..]); - keccak256(domain_input) - } -} - -/// Derives a deposit transaction from an EVM log event emitted by the deposit contract. -/// -/// The emitted log must be in format: -/// ```solidity -/// event TransactionDeposited( -/// address indexed from, -/// address indexed to, -/// uint256 indexed version, -/// bytes opaqueData -/// ); -/// ``` -pub fn decode_deposit(block_hash: B256, index: usize, log: &Log) -> Result { - let topics = log.data.topics(); - if topics.len() != 4 { - return Err(DepositError::UnexpectedTopicsLen(topics.len())); - } - if topics[0] != DEPOSIT_EVENT_ABI_HASH { - return Err(DepositError::InvalidSelector(DEPOSIT_EVENT_ABI_HASH, topics[0])); - } - if log.data.data.len() < 64 { - return Err(DepositError::IncompleteOpaqueData(log.data.data.len())); - } - if log.data.data.len() % 32 != 0 { - return Err(DepositError::UnalignedData(log.data.data.len())); - } - - let from = Address::try_from(&topics[1].as_slice()[12..]) - .map_err(|_| DepositError::FromDecode(topics[1]))?; - let to = Address::try_from(&topics[2].as_slice()[12..]) - .map_err(|_| DepositError::ToDecode(topics[2]))?; - let version = log.data.topics()[3]; - - // Solidity serializes the event's Data field as follows: - // - // ```solidity - // abi.encode(abi.encodPacked(uint256 mint, uint256 value, uint64 gasLimit, uint8 isCreation, bytes data)) - // ``` - // - // The the opaqueData will be packed as shown below: - // - // ------------------------------------------------------------ - // | offset | 256 byte content | - // ------------------------------------------------------------ - // | 0 | [0; 24] . {U64 big endian, hex encoded offset} | - // ------------------------------------------------------------ - // | 32 | [0; 24] . {U64 big endian, hex encoded length} | - // ------------------------------------------------------------ - - let opaque_content_offset: U64 = U64::try_from_be_slice(&log.data.data[24..32]).ok_or( - DepositError::InvalidOpaqueDataOffset(Bytes::copy_from_slice(&log.data.data[24..32])), - )?; - if opaque_content_offset != U64::from(32) { - return Err(DepositError::InvalidOpaqueDataOffset(Bytes::copy_from_slice( - &log.data.data[24..32], - ))); - } - - // The next 32 bytes indicate the length of the opaqueData content. - let opaque_content_len = - u64::from_be_bytes(log.data.data[56..64].try_into().map_err(|_| { - DepositError::InvalidOpaqueDataLength(Bytes::copy_from_slice(&log.data.data[56..64])) - })?); - if opaque_content_len as usize > log.data.data.len() - 64 { - return Err(DepositError::OpaqueDataOverflow( - opaque_content_len as usize, - log.data.data.len() - 64, - )); - } - let padded_len = opaque_content_len.checked_add(32).ok_or(DepositError::OpaqueDataOverflow( - opaque_content_len as usize, - log.data.data.len() - 64, - ))?; - if padded_len as usize <= log.data.data.len() - 64 { - return Err(DepositError::PaddedOpaqueDataOverflow( - log.data.data.len() - 64, - opaque_content_len as usize, - )); - } - - // The remaining data is the opaqueData which is tightly packed and then padded to 32 bytes by - // the EVM. - let opaque_data = &log.data.data[64..64 + opaque_content_len as usize]; - let source = UserDepositSource::new(block_hash, index as u64); - - let mut deposit_tx = TxDeposit { - from, - is_system_transaction: false, - source_hash: source.source_hash(), - ..Default::default() - }; - - // Can only handle version 0 for now - if !version.is_zero() { - return Err(DepositError::InvalidVersion(version)); - } - - unmarshal_deposit_version0(&mut deposit_tx, to, opaque_data)?; - - // Re-encode the deposit transaction - let deposit_envelope = OpTxEnvelope::Deposit(deposit_tx); - let mut buffer = Vec::with_capacity(deposit_envelope.length()); - deposit_envelope.encode_2718(&mut buffer); - Ok(Bytes::from(buffer)) -} - -/// Unmarshals a deposit transaction from the opaque data. -pub(crate) fn unmarshal_deposit_version0( - tx: &mut TxDeposit, - to: Address, - data: &[u8], -) -> Result<(), DepositError> { - if data.len() < 32 + 32 + 8 + 1 { - return Err(DepositError::UnexpectedOpaqueDataLen(data.len())); - } - - let mut offset = 0; - - let raw_mint: [u8; 16] = data[offset + 16..offset + 32].try_into().map_err(|_| { - DepositError::MintDecode(Bytes::copy_from_slice(&data[offset + 16..offset + 32])) - })?; - let mint = u128::from_be_bytes(raw_mint); - - // 0 mint is represented as nil to skip minting code - if mint == 0 { - tx.mint = None; - } else { - tx.mint = Some(mint); - } - offset += 32; - - // uint256 value - tx.value = U256::from_be_slice(&data[offset..offset + 32]); - offset += 32; - - // uint64 gas - let raw_gas: [u8; 8] = data[offset..offset + 8] - .try_into() - .map_err(|_| DepositError::GasDecode(Bytes::copy_from_slice(&data[offset..offset + 8])))?; - tx.gas_limit = u64::from_be_bytes(raw_gas) as u128; - offset += 8; - - // uint8 isCreation - // isCreation: If the boolean byte is 1 then dep.To will stay nil, - // and it will create a contract using L2 account nonce to determine the created address. - if data[offset] == 0 { - tx.to = TxKind::Call(to); - } else { - tx.to = TxKind::Create; - } - offset += 1; - - // The remainder of the opaqueData is the transaction data (without length prefix). - // The data may be padded to a multiple of 32 bytes - let tx_data_len = data.len() - offset; - - // Remaining bytes fill the data - tx.input = Bytes::copy_from_slice(&data[offset..offset + tx_data_len]); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use alloc::vec; - use alloy_primitives::{address, b256, hex, LogData}; - - #[test] - fn test_decode_deposit_invalid_topic_len() { - let log = Log { - address: Address::default(), - data: LogData::new_unchecked(vec![B256::default()], Bytes::default()), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::UnexpectedTopicsLen(1)); - } - - #[test] - fn test_decode_deposit_invalid_first_topic() { - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![B256::default(), B256::default(), B256::default(), B256::default()], - Bytes::default(), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::InvalidSelector(DEPOSIT_EVENT_ABI_HASH, B256::default())); - } - - #[test] - fn test_decode_deposit_incomplete_data() { - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(vec![0u8; 63]), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::IncompleteOpaqueData(63)); - } - - #[test] - fn test_decode_deposit_unaligned_data() { - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(vec![0u8; 65]), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::UnalignedData(65)); - } - - #[test] - #[ignore] - fn test_decode_deposit_invalid_from() {} - - #[test] - #[ignore] - fn test_decode_deposit_invalid_to() {} - - #[test] - fn test_decode_deposit_invalid_opaque_data_offset() { - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(vec![0u8; 64]), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::InvalidOpaqueDataOffset(Bytes::from(vec![0u8; 8]))); - } - - #[test] - fn test_decode_deposit_opaque_data_overflow() { - let mut data = vec![0u8; 128]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - // The first 64 bytes of the data are identifiers so - // if this test was to be valid, the data length would be 64 not 128. - let len: [u8; 8] = U64::from(128).to_be_bytes(); - data[56..64].copy_from_slice(&len); - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(data), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::OpaqueDataOverflow(128, 64)); - } - - #[test] - fn test_decode_deposit_padded_overflow() { - let mut data = vec![0u8; 256]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - let len: [u8; 8] = U64::from(64).to_be_bytes(); - data[56..64].copy_from_slice(&len); - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(data), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::PaddedOpaqueDataOverflow(192, 64)); - } - - #[test] - fn test_decode_deposit_invalid_version() { - let mut data = vec![0u8; 128]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - let len: [u8; 8] = U64::from(64).to_be_bytes(); - data[56..64].copy_from_slice(&len); - let version = b256!("0000000000000000000000000000000000000000000000000000000000000001"); - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), version], - Bytes::from(data), - ), - }; - let err = decode_deposit(B256::default(), 0, &log).unwrap_err(); - assert_eq!(err, DepositError::InvalidVersion(version)); - } - - #[test] - fn test_decode_deposit_empty_succeeds() { - let mut data = vec![0u8; 192]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - let len: [u8; 8] = U64::from(128).to_be_bytes(); - data[56..64].copy_from_slice(&len); - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()], - Bytes::from(data), - ), - }; - let tx = decode_deposit(B256::default(), 0, &log).unwrap(); - let raw_hex = hex!("7ef887a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a14594000000000000000000000000000000000000000094000000000000000000000000000000000000000080808080b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - let expected = Bytes::from(raw_hex); - assert_eq!(tx, expected); - } - - #[test] - fn test_decode_deposit_full_succeeds() { - let mut data = vec![0u8; 192]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - let len: [u8; 8] = U64::from(128).to_be_bytes(); - data[56..64].copy_from_slice(&len); - // Copy the u128 mint value - let mint: [u8; 16] = 10_u128.to_be_bytes(); - data[80..96].copy_from_slice(&mint); - // Copy the tx value - let value: [u8; 32] = U256::from(100).to_be_bytes(); - data[96..128].copy_from_slice(&value); - // Copy the gas limit - let gas: [u8; 8] = 1000_u64.to_be_bytes(); - data[128..136].copy_from_slice(&gas); - // Copy the isCreation flag - data[136] = 1; - let from = address!("1111111111111111111111111111111111111111"); - let mut from_bytes = vec![0u8; 32]; - from_bytes[12..32].copy_from_slice(from.as_slice()); - let to = address!("2222222222222222222222222222222222222222"); - let mut to_bytes = vec![0u8; 32]; - to_bytes[12..32].copy_from_slice(to.as_slice()); - let log = Log { - address: Address::default(), - data: LogData::new_unchecked( - vec![ - DEPOSIT_EVENT_ABI_HASH, - B256::from_slice(&from_bytes), - B256::from_slice(&to_bytes), - B256::default(), - ], - Bytes::from(data), - ), - }; - let tx = decode_deposit(B256::default(), 0, &log).unwrap(); - let raw_hex = hex!("7ef875a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a145941111111111111111111111111111111111111111800a648203e880b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - let expected = Bytes::from(raw_hex); - assert_eq!(tx, expected); - } - - #[test] - fn test_unmarshal_deposit_version0_invalid_len() { - let data = vec![0u8; 72]; - let mut tx = TxDeposit::default(); - let to = address!("5555555555555555555555555555555555555555"); - let err = unmarshal_deposit_version0(&mut tx, to, &data).unwrap_err(); - assert_eq!(err, DepositError::UnexpectedOpaqueDataLen(72)); - - // Data must have at least length 73 - let data = vec![0u8; 73]; - let mut tx = TxDeposit::default(); - let to = address!("5555555555555555555555555555555555555555"); - unmarshal_deposit_version0(&mut tx, to, &data).unwrap(); - } - - #[test] - fn test_unmarshal_deposit_version0() { - let mut data = vec![0u8; 192]; - let offset: [u8; 8] = U64::from(32).to_be_bytes(); - data[24..32].copy_from_slice(&offset); - let len: [u8; 8] = U64::from(128).to_be_bytes(); - data[56..64].copy_from_slice(&len); - // Copy the u128 mint value - let mint: [u8; 16] = 10_u128.to_be_bytes(); - data[80..96].copy_from_slice(&mint); - // Copy the tx value - let value: [u8; 32] = U256::from(100).to_be_bytes(); - data[96..128].copy_from_slice(&value); - // Copy the gas limit - let gas: [u8; 8] = 1000_u64.to_be_bytes(); - data[128..136].copy_from_slice(&gas); - // Copy the isCreation flag - data[136] = 1; - let mut tx = TxDeposit { - from: address!("1111111111111111111111111111111111111111"), - to: TxKind::Call(address!("2222222222222222222222222222222222222222")), - value: U256::from(100), - gas_limit: 1000, - mint: Some(10), - ..Default::default() - }; - let to = address!("5555555555555555555555555555555555555555"); - unmarshal_deposit_version0(&mut tx, to, &data).unwrap(); - assert_eq!(tx.to, TxKind::Call(address!("5555555555555555555555555555555555555555"))); - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 3d24045ebb..dbc75e5cef 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -29,6 +29,11 @@ pub use alloy_consensus::{ TxEip4844WithSidecar, TxEnvelope, TxLegacy, }; +pub use op_alloy_protocol::deposits::{ + decode_deposit, DepositError, DepositSourceDomain, DepositSourceDomainIdentifier, + L1InfoDepositSource, UpgradeDepositSource, UserDepositSource, DEPOSIT_EVENT_ABI_HASH, +}; + // Re-export `alloy-eips` eip4844 types. pub use alloy_eips::eip4844::{Blob, BYTES_PER_BLOB, VERSIONED_HASH_VERSION_KZG}; @@ -43,12 +48,6 @@ pub use block::{Block, BlockKind, OpBlock}; pub mod block_info; pub use block_info::{L1BlockInfoBedrock, L1BlockInfoEcotone, L1BlockInfoTx}; -pub mod deposits; -pub use deposits::{ - decode_deposit, DepositError, DepositSourceDomain, DepositSourceDomainIdentifier, - L1InfoDepositSource, UpgradeDepositSource, UserDepositSource, DEPOSIT_EVENT_ABI_HASH, -}; - pub mod payload; pub use payload::{ L2ExecutionPayload, L2ExecutionPayloadEnvelope, PAYLOAD_MEM_FIXED_COST, PAYLOAD_TX_MEM_OVERHEAD,