From 454274476f27967fc2e421adfc4ee66d83e3c6c6 Mon Sep 17 00:00:00 2001 From: Jordan Oroshiba Date: Mon, 24 Feb 2025 16:08:41 -0800 Subject: [PATCH 1/5] feat(conductor)!: include sequencer block hash --- crates/astria-conductor/CHANGELOG.md | 4 ++ .../astria-conductor/src/executor/client.rs | 2 + crates/astria-conductor/src/executor/mod.rs | 4 +- crates/astria-conductor/src/executor/state.rs | 2 + crates/astria-conductor/src/executor/tests.rs | 1 + .../tests/blackbox/helpers/macros.rs | 1 + crates/astria-core/src/execution/v1/mod.rs | 11 +++++ .../src/generated/astria.execution.v1.rs | 6 +++ .../generated/astria.execution.v1.serde.rs | 42 +++++++++++++++++++ .../src/generated/astria.execution.v2.rs | 8 ++++ .../generated/astria.execution.v2.serde.rs | 36 ++++++++++++++++ dev/values/rollup/dev.yaml | 11 ++++- .../astria/execution/v1/execution.proto | 4 ++ .../execution/v2/execute_block_request.proto | 3 ++ .../v2/executed_block_metadata.proto | 3 ++ specs/execution-api.md | 10 ++++- 16 files changed, 145 insertions(+), 3 deletions(-) diff --git a/crates/astria-conductor/CHANGELOG.md b/crates/astria-conductor/CHANGELOG.md index 2210b7b33e..ff282f95b3 100644 --- a/crates/astria-conductor/CHANGELOG.md +++ b/crates/astria-conductor/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `idna` dependency to resolve cargo audit warning [#1869](https://github.com/astriaorg/astria/pull/1869). - Remove panic source on shutdown [#1919](https://github.com/astriaorg/astria/pull/1919). +### Added + +- Add `sequencer_block_hash` to Execution API [#1999](https://github.com/astriaorg/astria/pull/1999). + ## [1.0.0] - 2024-10-25 ### Changed diff --git a/crates/astria-conductor/src/executor/client.rs b/crates/astria-conductor/src/executor/client.rs index 1bab377d97..ea118b4f77 100644 --- a/crates/astria-conductor/src/executor/client.rs +++ b/crates/astria-conductor/src/executor/client.rs @@ -119,6 +119,7 @@ impl Client { prev_block_hash: Bytes, transactions: Vec, timestamp: Timestamp, + sequencer_block_hash: Bytes, ) -> eyre::Result { use prost::Message; @@ -132,6 +133,7 @@ impl Client { prev_block_hash, transactions, timestamp: Some(timestamp), + sequencer_block_hash, }; let response = tryhard::retry_fn(|| { let mut client = self.inner.clone(); diff --git a/crates/astria-conductor/src/executor/mod.rs b/crates/astria-conductor/src/executor/mod.rs index ad475db85f..939fc30397 100644 --- a/crates/astria-conductor/src/executor/mod.rs +++ b/crates/astria-conductor/src/executor/mod.rs @@ -496,16 +496,18 @@ impl Initialized { block: ExecutableBlock, ) -> eyre::Result { let ExecutableBlock { + hash, transactions, timestamp, .. } = block; let n_transactions = transactions.len(); + let sequencer_block_hash = hash.as_bytes().to_vec().into(); let executed_block = self .client - .execute_block_with_retry(parent_hash, transactions, timestamp) + .execute_block_with_retry(parent_hash, transactions, timestamp, sequencer_block_hash) .await .wrap_err("failed to run execute_block RPC")?; diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index 1f315b078b..6475efafc3 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -347,6 +347,7 @@ mod tests { seconds: 123_456, nanos: 789, }), + sequencer_block_hash: vec![].into(), }) .unwrap(); let soft = Block::try_from_raw(raw::Block { @@ -357,6 +358,7 @@ mod tests { seconds: 123_456, nanos: 789, }), + sequencer_block_hash: vec![].into(), }) .unwrap(); CommitmentState::builder() diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index a5206cb141..5822eedca6 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -32,6 +32,7 @@ fn make_block(number: u32) -> raw::Block { seconds: 0, nanos: 0, }), + sequencer_block_hash: vec![].into(), } } diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index 981f4c31dc..c31ee516d0 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -9,6 +9,7 @@ macro_rules! block { seconds: 1, nanos: 1, }), + sequencer_block_hash: vec![].into(), } }; } diff --git a/crates/astria-core/src/execution/v1/mod.rs b/crates/astria-core/src/execution/v1/mod.rs index 3327cd0edc..b0964bbeae 100644 --- a/crates/astria-core/src/execution/v1/mod.rs +++ b/crates/astria-core/src/execution/v1/mod.rs @@ -159,6 +159,8 @@ pub struct Block { parent_block_hash: Bytes, /// Timestamp on the block, standardized to google protobuf standard. timestamp: Timestamp, + /// The hash of the sequencer block that this block is derived from. + sequencer_block_hash: Bytes, } impl Block { @@ -183,6 +185,11 @@ impl Block { // effectively just a copy self.timestamp.clone() } + + #[must_use] + pub fn sequencer_block_hash(&self) -> &Bytes { + &self.sequencer_block_hash + } } impl From for raw::Block { @@ -201,6 +208,7 @@ impl Protobuf for Block { hash, parent_block_hash, timestamp, + sequencer_block_hash, } = raw; // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) tuple let timestamp = timestamp @@ -212,6 +220,7 @@ impl Protobuf for Block { hash: hash.clone(), parent_block_hash: parent_block_hash.clone(), timestamp, + sequencer_block_hash: sequencer_block_hash.clone(), }) } @@ -221,6 +230,7 @@ impl Protobuf for Block { hash, parent_block_hash, timestamp, + sequencer_block_hash, } = self; Self::Raw { number: *number, @@ -229,6 +239,7 @@ impl Protobuf for Block { // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) // tuple timestamp: Some(timestamp.clone()), + sequencer_block_hash: sequencer_block_hash.clone(), } } } diff --git a/crates/astria-core/src/generated/astria.execution.v1.rs b/crates/astria-core/src/generated/astria.execution.v1.rs index 35b5d20480..0bd056782e 100644 --- a/crates/astria-core/src/generated/astria.execution.v1.rs +++ b/crates/astria-core/src/generated/astria.execution.v1.rs @@ -39,6 +39,9 @@ pub struct Block { /// Timestamp on the block, standardized to google protobuf standard. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// The block hash of sequencer block this is derived from. + #[prost(bytes = "bytes", tag = "5")] + pub sequencer_block_hash: ::prost::bytes::Bytes, } impl ::prost::Name for Block { const NAME: &'static str = "Block"; @@ -144,6 +147,9 @@ pub struct ExecuteBlockRequest { /// Timestamp to be used for new block. #[prost(message, optional, tag = "3")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// The hash of the sequencer block the transactions come from. + #[prost(bytes = "bytes", tag = "4")] + pub sequencer_block_hash: ::prost::bytes::Bytes, } impl ::prost::Name for ExecuteBlockRequest { const NAME: &'static str = "ExecuteBlockRequest"; diff --git a/crates/astria-core/src/generated/astria.execution.v1.serde.rs b/crates/astria-core/src/generated/astria.execution.v1.serde.rs index 970146cc46..22eb789e89 100644 --- a/crates/astria-core/src/generated/astria.execution.v1.serde.rs +++ b/crates/astria-core/src/generated/astria.execution.v1.serde.rs @@ -200,6 +200,9 @@ impl serde::Serialize for Block { if self.timestamp.is_some() { len += 1; } + if !self.sequencer_block_hash.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("astria.execution.v1.Block", len)?; if self.number != 0 { struct_ser.serialize_field("number", &self.number)?; @@ -215,6 +218,10 @@ impl serde::Serialize for Block { if let Some(v) = self.timestamp.as_ref() { struct_ser.serialize_field("timestamp", v)?; } + if !self.sequencer_block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("sequencerBlockHash", pbjson::private::base64::encode(&self.sequencer_block_hash).as_str())?; + } struct_ser.end() } } @@ -230,6 +237,8 @@ impl<'de> serde::Deserialize<'de> for Block { "parent_block_hash", "parentBlockHash", "timestamp", + "sequencer_block_hash", + "sequencerBlockHash", ]; #[allow(clippy::enum_variant_names)] @@ -238,6 +247,7 @@ impl<'de> serde::Deserialize<'de> for Block { Hash, ParentBlockHash, Timestamp, + SequencerBlockHash, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -263,6 +273,7 @@ impl<'de> serde::Deserialize<'de> for Block { "hash" => Ok(GeneratedField::Hash), "parentBlockHash" | "parent_block_hash" => Ok(GeneratedField::ParentBlockHash), "timestamp" => Ok(GeneratedField::Timestamp), + "sequencerBlockHash" | "sequencer_block_hash" => Ok(GeneratedField::SequencerBlockHash), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -286,6 +297,7 @@ impl<'de> serde::Deserialize<'de> for Block { let mut hash__ = None; let mut parent_block_hash__ = None; let mut timestamp__ = None; + let mut sequencer_block_hash__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Number => { @@ -318,6 +330,14 @@ impl<'de> serde::Deserialize<'de> for Block { } timestamp__ = map_.next_value()?; } + GeneratedField::SequencerBlockHash => { + if sequencer_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("sequencerBlockHash")); + } + sequencer_block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } } } Ok(Block { @@ -325,6 +345,7 @@ impl<'de> serde::Deserialize<'de> for Block { hash: hash__.unwrap_or_default(), parent_block_hash: parent_block_hash__.unwrap_or_default(), timestamp: timestamp__, + sequencer_block_hash: sequencer_block_hash__.unwrap_or_default(), }) } } @@ -587,6 +608,9 @@ impl serde::Serialize for ExecuteBlockRequest { if self.timestamp.is_some() { len += 1; } + if !self.sequencer_block_hash.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("astria.execution.v1.ExecuteBlockRequest", len)?; if !self.prev_block_hash.is_empty() { #[allow(clippy::needless_borrow)] @@ -598,6 +622,10 @@ impl serde::Serialize for ExecuteBlockRequest { if let Some(v) = self.timestamp.as_ref() { struct_ser.serialize_field("timestamp", v)?; } + if !self.sequencer_block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("sequencerBlockHash", pbjson::private::base64::encode(&self.sequencer_block_hash).as_str())?; + } struct_ser.end() } } @@ -612,6 +640,8 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { "prevBlockHash", "transactions", "timestamp", + "sequencer_block_hash", + "sequencerBlockHash", ]; #[allow(clippy::enum_variant_names)] @@ -619,6 +649,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { PrevBlockHash, Transactions, Timestamp, + SequencerBlockHash, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -643,6 +674,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { "prevBlockHash" | "prev_block_hash" => Ok(GeneratedField::PrevBlockHash), "transactions" => Ok(GeneratedField::Transactions), "timestamp" => Ok(GeneratedField::Timestamp), + "sequencerBlockHash" | "sequencer_block_hash" => Ok(GeneratedField::SequencerBlockHash), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -665,6 +697,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { let mut prev_block_hash__ = None; let mut transactions__ = None; let mut timestamp__ = None; + let mut sequencer_block_hash__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::PrevBlockHash => { @@ -687,12 +720,21 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { } timestamp__ = map_.next_value()?; } + GeneratedField::SequencerBlockHash => { + if sequencer_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("sequencerBlockHash")); + } + sequencer_block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } } } Ok(ExecuteBlockRequest { prev_block_hash: prev_block_hash__.unwrap_or_default(), transactions: transactions__.unwrap_or_default(), timestamp: timestamp__, + sequencer_block_hash: sequencer_block_hash__.unwrap_or_default(), }) } } diff --git a/crates/astria-core/src/generated/astria.execution.v2.rs b/crates/astria-core/src/generated/astria.execution.v2.rs index 184c751a96..25bf826a70 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.rs @@ -17,6 +17,10 @@ pub struct ExecutedBlockMetadata { /// was constructed from. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// The block hash of the sequencer block which this execution block is derived + /// from. Utilizing this field is optional. + #[prost(string, tag = "5")] + pub sequencer_block_hash: ::prost::alloc::string::String, } impl ::prost::Name for ExecutedBlockMetadata { const NAME: &'static str = "ExecutedBlockMetadata"; @@ -92,6 +96,10 @@ pub struct ExecuteBlockRequest { /// Timestamp to be used for new block. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// The block hash of the sequencer which this block is derived from. + /// Utilizing this field is optional for the execution node. + #[prost(string, tag = "5")] + pub sequencer_block_hash: ::prost::alloc::string::String, } impl ::prost::Name for ExecuteBlockRequest { const NAME: &'static str = "ExecuteBlockRequest"; diff --git a/crates/astria-core/src/generated/astria.execution.v2.serde.rs b/crates/astria-core/src/generated/astria.execution.v2.serde.rs index 5329d0d55e..e03f5c1cbc 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.serde.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.serde.rs @@ -220,6 +220,9 @@ impl serde::Serialize for ExecuteBlockRequest { if self.timestamp.is_some() { len += 1; } + if !self.sequencer_block_hash.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.ExecuteBlockRequest", len)?; if !self.session_id.is_empty() { struct_ser.serialize_field("sessionId", &self.session_id)?; @@ -233,6 +236,9 @@ impl serde::Serialize for ExecuteBlockRequest { if let Some(v) = self.timestamp.as_ref() { struct_ser.serialize_field("timestamp", v)?; } + if !self.sequencer_block_hash.is_empty() { + struct_ser.serialize_field("sequencerBlockHash", &self.sequencer_block_hash)?; + } struct_ser.end() } } @@ -249,6 +255,8 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { "parentHash", "transactions", "timestamp", + "sequencer_block_hash", + "sequencerBlockHash", ]; #[allow(clippy::enum_variant_names)] @@ -257,6 +265,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { ParentHash, Transactions, Timestamp, + SequencerBlockHash, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -282,6 +291,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { "parentHash" | "parent_hash" => Ok(GeneratedField::ParentHash), "transactions" => Ok(GeneratedField::Transactions), "timestamp" => Ok(GeneratedField::Timestamp), + "sequencerBlockHash" | "sequencer_block_hash" => Ok(GeneratedField::SequencerBlockHash), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -305,6 +315,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { let mut parent_hash__ = None; let mut transactions__ = None; let mut timestamp__ = None; + let mut sequencer_block_hash__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::SessionId => { @@ -331,6 +342,12 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { } timestamp__ = map_.next_value()?; } + GeneratedField::SequencerBlockHash => { + if sequencer_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("sequencerBlockHash")); + } + sequencer_block_hash__ = Some(map_.next_value()?); + } } } Ok(ExecuteBlockRequest { @@ -338,6 +355,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { parent_hash: parent_hash__.unwrap_or_default(), transactions: transactions__.unwrap_or_default(), timestamp: timestamp__, + sequencer_block_hash: sequencer_block_hash__.unwrap_or_default(), }) } } @@ -564,6 +582,9 @@ impl serde::Serialize for ExecutedBlockMetadata { if self.timestamp.is_some() { len += 1; } + if !self.sequencer_block_hash.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.ExecutedBlockMetadata", len)?; if self.number != 0 { #[allow(clippy::needless_borrow)] @@ -578,6 +599,9 @@ impl serde::Serialize for ExecutedBlockMetadata { if let Some(v) = self.timestamp.as_ref() { struct_ser.serialize_field("timestamp", v)?; } + if !self.sequencer_block_hash.is_empty() { + struct_ser.serialize_field("sequencerBlockHash", &self.sequencer_block_hash)?; + } struct_ser.end() } } @@ -593,6 +617,8 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { "parent_hash", "parentHash", "timestamp", + "sequencer_block_hash", + "sequencerBlockHash", ]; #[allow(clippy::enum_variant_names)] @@ -601,6 +627,7 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { Hash, ParentHash, Timestamp, + SequencerBlockHash, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -626,6 +653,7 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { "hash" => Ok(GeneratedField::Hash), "parentHash" | "parent_hash" => Ok(GeneratedField::ParentHash), "timestamp" => Ok(GeneratedField::Timestamp), + "sequencerBlockHash" | "sequencer_block_hash" => Ok(GeneratedField::SequencerBlockHash), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -649,6 +677,7 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { let mut hash__ = None; let mut parent_hash__ = None; let mut timestamp__ = None; + let mut sequencer_block_hash__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Number => { @@ -677,6 +706,12 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { } timestamp__ = map_.next_value()?; } + GeneratedField::SequencerBlockHash => { + if sequencer_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("sequencerBlockHash")); + } + sequencer_block_hash__ = Some(map_.next_value()?); + } } } Ok(ExecutedBlockMetadata { @@ -684,6 +719,7 @@ impl<'de> serde::Deserialize<'de> for ExecutedBlockMetadata { hash: hash__.unwrap_or_default(), parent_hash: parent_hash__.unwrap_or_default(), timestamp: timestamp__, + sequencer_block_hash: sequencer_block_hash__.unwrap_or_default(), }) } } diff --git a/dev/values/rollup/dev.yaml b/dev/values/rollup/dev.yaml index 66eb3afb71..09e4195781 100644 --- a/dev/values/rollup/dev.yaml +++ b/dev/values/rollup/dev.yaml @@ -9,6 +9,9 @@ global: celestiaChainId: celestia-local-0 evm-rollup: + images: + geth: + devTag: sha-76bc884 genesis: ## These values are used to configure the genesis block of the rollup chain ## no defaults as they are unique to each chain @@ -61,7 +64,7 @@ evm-rollup: ## Standard Eth Genesis config values # Configuration of Eth forks, setting to 0 will enable from height, # left as is these forks will not activate. - cancunTime: "" + cancunTime: "0" pragueTime: "" verkleTime: "" # Can configure the genesis allocs for the chain @@ -74,6 +77,12 @@ evm-rollup: value: balance: "0" code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3" + # EIP-4877 Beacon Block Contract + - address: "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" + value: + balance: "0" + code: "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500" + # The EVM Withdrawer contract - address: "0xA58639fB5458e65E4fA917FF951C390292C24A15" value: balance: "0" diff --git a/proto/executionapis/astria/execution/v1/execution.proto b/proto/executionapis/astria/execution/v1/execution.proto index e68083db4d..01ec0c9387 100644 --- a/proto/executionapis/astria/execution/v1/execution.proto +++ b/proto/executionapis/astria/execution/v1/execution.proto @@ -30,6 +30,8 @@ message Block { bytes parent_block_hash = 3; // Timestamp on the block, standardized to google protobuf standard. google.protobuf.Timestamp timestamp = 4; + // The block hash of sequencer block this is derived from. + bytes sequencer_block_hash = 5; } // Fields which are indexed for finding blocks on a blockchain. @@ -70,6 +72,8 @@ message ExecuteBlockRequest { repeated astria.sequencerblock.v1.RollupData transactions = 2; // Timestamp to be used for new block. google.protobuf.Timestamp timestamp = 3; + // The hash of the sequencer block the transactions come from. + bytes sequencer_block_hash = 4; } // The CommitmentState holds the block at each stage of sequencer commitment diff --git a/proto/executionapis/astria/execution/v2/execute_block_request.proto b/proto/executionapis/astria/execution/v2/execute_block_request.proto index 90b6dafac7..81918d4901 100644 --- a/proto/executionapis/astria/execution/v2/execute_block_request.proto +++ b/proto/executionapis/astria/execution/v2/execute_block_request.proto @@ -20,4 +20,7 @@ message ExecuteBlockRequest { repeated astria.sequencerblock.v1.RollupData transactions = 3; // Timestamp to be used for new block. google.protobuf.Timestamp timestamp = 4; + // The block hash of the sequencer which this block is derived from. + // Utilizing this field is optional for the execution node. + string sequencer_block_hash = 5; } diff --git a/proto/executionapis/astria/execution/v2/executed_block_metadata.proto b/proto/executionapis/astria/execution/v2/executed_block_metadata.proto index 2a09f1ebb4..5a3a70628f 100644 --- a/proto/executionapis/astria/execution/v2/executed_block_metadata.proto +++ b/proto/executionapis/astria/execution/v2/executed_block_metadata.proto @@ -17,4 +17,7 @@ message ExecutedBlockMetadata { // Timestamp of the block, taken from the sequencer block that this rollup block // was constructed from. google.protobuf.Timestamp timestamp = 4; + // The block hash of the sequencer block which this execution block is derived + // from. Utilizing this field is optional. + string sequencer_block_hash = 5; } diff --git a/specs/execution-api.md b/specs/execution-api.md index c765229007..fc9a8d7021 100644 --- a/specs/execution-api.md +++ b/specs/execution-api.md @@ -86,7 +86,15 @@ indicated by `prev_block_hash`. The following should be respected: - `prev_block_hash` MUST match hash of the `SOFT` commitment state block, return `FAILED_PRECONDITION` otherwise. - If block headers have timestamps, created block MUST have matching timestamp -- The CommitmentState is NOT modified by the execution of the block. +- The `CommitmentState` is NOT modified by the execution of the block. +- It is up to the execution node if it includes the `sequencer_block_hash` + provided as a part of the block. If utilized the server MUST throw an + `INVALID_ARGUMENT` error if the `sequencer_block_hash` is not included in the + request.\ + \ + This field is an addition to the original API, running old versions of the + client which excluded it would otherwise create non-deterministic blocks + between nodes running different API client software. ### GetBlock From d30cbefb86c998d1d0c67ec589dbb232e95c4108 Mon Sep 17 00:00:00 2001 From: Jordan Oroshiba Date: Wed, 5 Mar 2025 16:58:36 -0800 Subject: [PATCH 2/5] use pr tag --- dev/values/rollup/dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/values/rollup/dev.yaml b/dev/values/rollup/dev.yaml index 09e4195781..eee01d27f6 100644 --- a/dev/values/rollup/dev.yaml +++ b/dev/values/rollup/dev.yaml @@ -11,7 +11,7 @@ global: evm-rollup: images: geth: - devTag: sha-76bc884 + devTag: pr-75 genesis: ## These values are used to configure the genesis block of the rollup chain ## no defaults as they are unique to each chain From 1de3d4b02240e08fe8d1960f91e65769eb31987b Mon Sep 17 00:00:00 2001 From: Jordan Oroshiba Date: Thu, 6 Mar 2025 09:55:54 -0800 Subject: [PATCH 3/5] add to flame dev --- dev/values/rollup/dev.yaml | 1 + dev/values/rollup/flame-dev.yaml | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dev/values/rollup/dev.yaml b/dev/values/rollup/dev.yaml index eee01d27f6..25a69a54c8 100644 --- a/dev/values/rollup/dev.yaml +++ b/dev/values/rollup/dev.yaml @@ -78,6 +78,7 @@ evm-rollup: balance: "0" code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3" # EIP-4877 Beacon Block Contract + # Code is defined by the contract in the spec: https://eips.ethereum.org/EIPS/eip-4788 - address: "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" value: balance: "0" diff --git a/dev/values/rollup/flame-dev.yaml b/dev/values/rollup/flame-dev.yaml index 5eb58554fd..efc203a1ad 100644 --- a/dev/values/rollup/flame-dev.yaml +++ b/dev/values/rollup/flame-dev.yaml @@ -13,6 +13,9 @@ evm-rollup: flame-rollup: enabled: true + images: + geth: + devTag: pr-49 genesis: ## These values are used to configure the genesis block of the rollup chain ## no defaults as they are unique to each chain @@ -66,7 +69,7 @@ flame-rollup: ## Standard Eth Genesis config values # Configuration of Eth forks, setting to 0 will enable from height, # left as is these forks will not activate. - cancunTime: "" + cancunTime: "0" pragueTime: "" verkleTime: "" # Can configure the genesis allocs for the chain @@ -79,6 +82,12 @@ flame-rollup: value: balance: "0" code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3" + # EIP-4877 Beacon Block Contract + # Code is defined by the contract in the spec: https://eips.ethereum.org/EIPS/eip-4788 + - address: "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" + value: + balance: "0" + code: "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500" - address: "0xA58639fB5458e65E4fA917FF951C390292C24A15" value: balance: "0" From f5aefeb71091250efd77e446c484e4b6dc0875e2 Mon Sep 17 00:00:00 2001 From: Jordan Oroshiba Date: Thu, 6 Mar 2025 10:26:43 -0800 Subject: [PATCH 4/5] review updates --- crates/astria-conductor/CHANGELOG.md | 2 +- crates/astria-conductor/src/executor/state.rs | 4 ++-- crates/astria-conductor/src/executor/tests.rs | 2 +- .../astria-conductor/tests/blackbox/helpers/macros.rs | 2 +- .../executionapis/astria/execution/v1/execution.proto | 10 ++++++++-- .../astria/execution/v2/execute_block_request.proto | 6 +++++- .../astria/execution/v2/executed_block_metadata.proto | 8 ++++++-- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/astria-conductor/CHANGELOG.md b/crates/astria-conductor/CHANGELOG.md index ff282f95b3..a954f26464 100644 --- a/crates/astria-conductor/CHANGELOG.md +++ b/crates/astria-conductor/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `sequencer_block_hash` to Execution API [#1999](https://github.com/astriaorg/astria/pull/1999). +- Send `sequencer_block_hash` as part of `ExecuteBlockRequest` [#1999](https://github.com/astriaorg/astria/pull/1999). ## [1.0.0] - 2024-10-25 diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index 6475efafc3..439aac570f 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -347,7 +347,7 @@ mod tests { seconds: 123_456, nanos: 789, }), - sequencer_block_hash: vec![].into(), + sequencer_block_hash: Bytes::new(), }) .unwrap(); let soft = Block::try_from_raw(raw::Block { @@ -358,7 +358,7 @@ mod tests { seconds: 123_456, nanos: 789, }), - sequencer_block_hash: vec![].into(), + sequencer_block_hash: Bytes::new(), }) .unwrap(); CommitmentState::builder() diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index 5822eedca6..79e5f418e8 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -32,7 +32,7 @@ fn make_block(number: u32) -> raw::Block { seconds: 0, nanos: 0, }), - sequencer_block_hash: vec![].into(), + sequencer_block_hash: Bytes::new(), } } diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index c31ee516d0..388ead3192 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -9,7 +9,7 @@ macro_rules! block { seconds: 1, nanos: 1, }), - sequencer_block_hash: vec![].into(), + sequencer_block_hash: Bytes::new(), } }; } diff --git a/proto/executionapis/astria/execution/v1/execution.proto b/proto/executionapis/astria/execution/v1/execution.proto index 01ec0c9387..3af88ef59f 100644 --- a/proto/executionapis/astria/execution/v1/execution.proto +++ b/proto/executionapis/astria/execution/v1/execution.proto @@ -30,7 +30,10 @@ message Block { bytes parent_block_hash = 3; // Timestamp on the block, standardized to google protobuf standard. google.protobuf.Timestamp timestamp = 4; - // The block hash of sequencer block this is derived from. + // The hash of the sequencer block from which this block was derived. + // + // (Optional) This field will only be utilized if the execution node stores + // this data in blocks during `ExecuteBlock`. bytes sequencer_block_hash = 5; } @@ -72,7 +75,10 @@ message ExecuteBlockRequest { repeated astria.sequencerblock.v1.RollupData transactions = 2; // Timestamp to be used for new block. google.protobuf.Timestamp timestamp = 3; - // The hash of the sequencer block the transactions come from. + // The hash of the sequencer block from which the transactions and timestamp + // are derived. + // + // Utilizing this field is optional for the execution node. bytes sequencer_block_hash = 4; } diff --git a/proto/executionapis/astria/execution/v2/execute_block_request.proto b/proto/executionapis/astria/execution/v2/execute_block_request.proto index 81918d4901..97a3d6e884 100644 --- a/proto/executionapis/astria/execution/v2/execute_block_request.proto +++ b/proto/executionapis/astria/execution/v2/execute_block_request.proto @@ -20,7 +20,11 @@ message ExecuteBlockRequest { repeated astria.sequencerblock.v1.RollupData transactions = 3; // Timestamp to be used for new block. google.protobuf.Timestamp timestamp = 4; - // The block hash of the sequencer which this block is derived from. + // The hash of the sequencer block from which the transactions and timestamp + // are derived. + // + // Must be a 32 byte base16 encoded string. It may be prefixed with `0x`. + // // Utilizing this field is optional for the execution node. string sequencer_block_hash = 5; } diff --git a/proto/executionapis/astria/execution/v2/executed_block_metadata.proto b/proto/executionapis/astria/execution/v2/executed_block_metadata.proto index 5a3a70628f..5b51fb810c 100644 --- a/proto/executionapis/astria/execution/v2/executed_block_metadata.proto +++ b/proto/executionapis/astria/execution/v2/executed_block_metadata.proto @@ -17,7 +17,11 @@ message ExecutedBlockMetadata { // Timestamp of the block, taken from the sequencer block that this rollup block // was constructed from. google.protobuf.Timestamp timestamp = 4; - // The block hash of the sequencer block which this execution block is derived - // from. Utilizing this field is optional. + // The hash of the sequencer block from which this block was derived. + // + // Must be 32 byte base16 encoded string. It may be prefixed with `0x`. + // + // (Optional) This field will only be utilized if the execution node stores + // this data in blocks during `ExecuteBlock`. string sequencer_block_hash = 5; } From a9c471206b48cfffaa453e080afcd8cde51b49bc Mon Sep 17 00:00:00 2001 From: Jordan Oroshiba Date: Thu, 6 Mar 2025 10:48:23 -0800 Subject: [PATCH 5/5] tests --- .../tests/blackbox/helpers/macros.rs | 2 +- .../src/generated/astria.execution.v1.rs | 10 ++++++++-- .../src/generated/astria.execution.v2.rs | 14 +++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index 388ead3192..40d19a5ba3 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -9,7 +9,7 @@ macro_rules! block { seconds: 1, nanos: 1, }), - sequencer_block_hash: Bytes::new(), + sequencer_block_hash: ::bytes::Bytes::new(), } }; } diff --git a/crates/astria-core/src/generated/astria.execution.v1.rs b/crates/astria-core/src/generated/astria.execution.v1.rs index 0bd056782e..fd8c5a44fb 100644 --- a/crates/astria-core/src/generated/astria.execution.v1.rs +++ b/crates/astria-core/src/generated/astria.execution.v1.rs @@ -39,7 +39,10 @@ pub struct Block { /// Timestamp on the block, standardized to google protobuf standard. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, - /// The block hash of sequencer block this is derived from. + /// The hash of the sequencer block from which this block was derived. + /// + /// (Optional) This field will only be utilized if the execution node stores + /// this data in blocks during `ExecuteBlock`. #[prost(bytes = "bytes", tag = "5")] pub sequencer_block_hash: ::prost::bytes::Bytes, } @@ -147,7 +150,10 @@ pub struct ExecuteBlockRequest { /// Timestamp to be used for new block. #[prost(message, optional, tag = "3")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, - /// The hash of the sequencer block the transactions come from. + /// The hash of the sequencer block from which the transactions and timestamp + /// are derived. + /// + /// Utilizing this field is optional for the execution node. #[prost(bytes = "bytes", tag = "4")] pub sequencer_block_hash: ::prost::bytes::Bytes, } diff --git a/crates/astria-core/src/generated/astria.execution.v2.rs b/crates/astria-core/src/generated/astria.execution.v2.rs index 25bf826a70..3d9368e692 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.rs @@ -17,8 +17,12 @@ pub struct ExecutedBlockMetadata { /// was constructed from. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, - /// The block hash of the sequencer block which this execution block is derived - /// from. Utilizing this field is optional. + /// The hash of the sequencer block from which this block was derived. + /// + /// Must be 32 byte base16 encoded string. It may be prefixed with `0x`. + /// + /// (Optional) This field will only be utilized if the execution node stores + /// this data in blocks during `ExecuteBlock`. #[prost(string, tag = "5")] pub sequencer_block_hash: ::prost::alloc::string::String, } @@ -96,7 +100,11 @@ pub struct ExecuteBlockRequest { /// Timestamp to be used for new block. #[prost(message, optional, tag = "4")] pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, - /// The block hash of the sequencer which this block is derived from. + /// The hash of the sequencer block from which the transactions and timestamp + /// are derived. + /// + /// Must be a 32 byte base16 encoded string. It may be prefixed with `0x`. + /// /// Utilizing this field is optional for the execution node. #[prost(string, tag = "5")] pub sequencer_block_hash: ::prost::alloc::string::String,