From 43349dbc16cfe6b977e160906d0aa3ee322ca5f9 Mon Sep 17 00:00:00 2001 From: Itamar Reif <9663129+itamarreif@users.noreply.github.com> Date: Tue, 19 Nov 2024 08:26:21 +0200 Subject: [PATCH 01/73] chore(proto): move optimistic block protos to v1 (#1707) ## Summary This updates the protos used for the auctioneer to be based on the new v1 protos. ## Background We bumped our proto definition versions from `v1alpha_` to `v1`. ## Changes - Moved the `sequencerblock/optimistic_block` file into its own subpackage in order to have `sequencerblock.optimisticblock.v1alpha1` - Updated to use `FilteredSequencerBlock` from v1 ## Breaking Changelist - This shouldn't break anything as none of these protos are used by anything in main currently. closes https://github.com/astriaorg/astria/issues/1767 --- ...tria.sequencerblock.optimistic.v1alpha1.rs | 516 ++++++++++++++++++ ...equencerblock.optimistic.v1alpha1.serde.rs | 460 ++++++++++++++++ ...sequencerblock.optimisticblock.v1alpha1.rs | 516 ++++++++++++++++++ ...cerblock.optimisticblock.v1alpha1.serde.rs | 460 ++++++++++++++++ .../astria.sequencerblock.v1alpha1.rs | 506 ----------------- .../astria.sequencerblock.v1alpha1.serde.rs | 460 ---------------- crates/astria-core/src/generated/mod.rs | 12 + .../v1alpha1/service.proto} | 6 +- 8 files changed, 1967 insertions(+), 969 deletions(-) create mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.rs create mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.serde.rs create mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs create mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs rename proto/sequencerblockapis/astria/sequencerblock/{v1alpha1/optimistic_block.proto => optimistic/v1alpha1/service.proto} (88%) diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.rs new file mode 100644 index 0000000000..b52cde36b9 --- /dev/null +++ b/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.rs @@ -0,0 +1,516 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBlockCommitmentStreamRequest {} +impl ::prost::Name for GetBlockCommitmentStreamRequest { + const NAME: &'static str = "GetBlockCommitmentStreamRequest"; + const PACKAGE: &'static str = "astria.sequencerblock.optimistic.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimistic.v1alpha1.{}", Self::NAME + ) + } +} +/// Identifying metadata for blocks that have been successfully committed in the Sequencer. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SequencerBlockCommit { + /// Height of the sequencer block that was committed. + #[prost(uint64, tag = "1")] + pub height: u64, + /// Hash of the sequencer block that was committed. + #[prost(bytes = "bytes", tag = "2")] + pub block_hash: ::prost::bytes::Bytes, +} +impl ::prost::Name for SequencerBlockCommit { + const NAME: &'static str = "SequencerBlockCommit"; + const PACKAGE: &'static str = "astria.sequencerblock.optimistic.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimistic.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBlockCommitmentStreamResponse { + #[prost(message, optional, tag = "1")] + pub commitment: ::core::option::Option, +} +impl ::prost::Name for GetBlockCommitmentStreamResponse { + const NAME: &'static str = "GetBlockCommitmentStreamResponse"; + const PACKAGE: &'static str = "astria.sequencerblock.optimistic.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimistic.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetOptimisticBlockStreamRequest { + /// The rollup id for which the Sequencer block is being streamed. + #[prost(message, optional, tag = "1")] + pub rollup_id: ::core::option::Option, +} +impl ::prost::Name for GetOptimisticBlockStreamRequest { + const NAME: &'static str = "GetOptimisticBlockStreamRequest"; + const PACKAGE: &'static str = "astria.sequencerblock.optimistic.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimistic.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetOptimisticBlockStreamResponse { + /// The optimistic Sequencer block that is being streamed, filtered for the provided rollup id. + #[prost(message, optional, tag = "1")] + pub block: ::core::option::Option, +} +impl ::prost::Name for GetOptimisticBlockStreamResponse { + const NAME: &'static str = "GetOptimisticBlockStreamResponse"; + const PACKAGE: &'static str = "astria.sequencerblock.optimistic.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimistic.v1alpha1.{}", Self::NAME + ) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod optimistic_block_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// The Sequencer will serve this to the aucitoneer + #[derive(Debug, Clone)] + pub struct OptimisticBlockServiceClient { + inner: tonic::client::Grpc, + } + impl OptimisticBlockServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl OptimisticBlockServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> OptimisticBlockServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + OptimisticBlockServiceClient::new( + InterceptedService::new(inner, interceptor), + ) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided + /// rollup id) to the Auctioneer. + pub async fn get_optimistic_block_stream( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService", + "GetOptimisticBlockStream", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + /// The Sequencer will stream the block commits to the Auctioneer. + pub async fn get_block_commitment_stream( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService", + "GetBlockCommitmentStream", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod optimistic_block_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with OptimisticBlockServiceServer. + #[async_trait] + pub trait OptimisticBlockService: Send + Sync + 'static { + /// Server streaming response type for the GetOptimisticBlockStream method. + type GetOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::GetOptimisticBlockStreamResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided + /// rollup id) to the Auctioneer. + async fn get_optimistic_block_stream( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetBlockCommitmentStream method. + type GetBlockCommitmentStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::GetBlockCommitmentStreamResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// The Sequencer will stream the block commits to the Auctioneer. + async fn get_block_commitment_stream( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// The Sequencer will serve this to the aucitoneer + #[derive(Debug)] + pub struct OptimisticBlockServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl OptimisticBlockServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> + for OptimisticBlockServiceServer + where + T: OptimisticBlockService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream" => { + #[allow(non_camel_case_types)] + struct GetOptimisticBlockStreamSvc( + pub Arc, + ); + impl< + T: OptimisticBlockService, + > tonic::server::ServerStreamingService< + super::GetOptimisticBlockStreamRequest, + > for GetOptimisticBlockStreamSvc { + type Response = super::GetOptimisticBlockStreamResponse; + type ResponseStream = T::GetOptimisticBlockStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GetOptimisticBlockStreamRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_optimistic_block_stream( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetOptimisticBlockStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream" => { + #[allow(non_camel_case_types)] + struct GetBlockCommitmentStreamSvc( + pub Arc, + ); + impl< + T: OptimisticBlockService, + > tonic::server::ServerStreamingService< + super::GetBlockCommitmentStreamRequest, + > for GetBlockCommitmentStreamSvc { + type Response = super::GetBlockCommitmentStreamResponse; + type ResponseStream = T::GetBlockCommitmentStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GetBlockCommitmentStreamRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block_commitment_stream( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockCommitmentStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for OptimisticBlockServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService + for OptimisticBlockServiceServer { + const NAME: &'static str = "astria.sequencerblock.optimistic.v1alpha1.OptimisticBlockService"; + } +} diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.serde.rs new file mode 100644 index 0000000000..816fc26a6c --- /dev/null +++ b/crates/astria-core/src/generated/astria.sequencerblock.optimistic.v1alpha1.serde.rs @@ -0,0 +1,460 @@ +impl serde::Serialize for GetBlockCommitmentStreamRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetBlockCommitmentStreamRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetBlockCommitmentStreamRequest { + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetBlockCommitmentStreamResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.commitment.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamResponse", len)?; + if let Some(v) = self.commitment.as_ref() { + struct_ser.serialize_field("commitment", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "commitment", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Commitment, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "commitment" => Ok(GeneratedField::Commitment), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetBlockCommitmentStreamResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut commitment__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Commitment => { + if commitment__.is_some() { + return Err(serde::de::Error::duplicate_field("commitment")); + } + commitment__ = map_.next_value()?; + } + } + } + Ok(GetBlockCommitmentStreamResponse { + commitment: commitment__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetBlockCommitmentStreamResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetOptimisticBlockStreamRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.rollup_id.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamRequest", len)?; + if let Some(v) = self.rollup_id.as_ref() { + struct_ser.serialize_field("rollupId", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "rollup_id", + "rollupId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + RollupId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetOptimisticBlockStreamRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut rollup_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::RollupId => { + if rollup_id__.is_some() { + return Err(serde::de::Error::duplicate_field("rollupId")); + } + rollup_id__ = map_.next_value()?; + } + } + } + Ok(GetOptimisticBlockStreamRequest { + rollup_id: rollup_id__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetOptimisticBlockStreamResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.block.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamResponse", len)?; + if let Some(v) = self.block.as_ref() { + struct_ser.serialize_field("block", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "block", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Block, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "block" => Ok(GeneratedField::Block), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetOptimisticBlockStreamResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut block__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Block => { + if block__.is_some() { + return Err(serde::de::Error::duplicate_field("block")); + } + block__ = map_.next_value()?; + } + } + } + Ok(GetOptimisticBlockStreamResponse { + block: block__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimistic.v1alpha1.GetOptimisticBlockStreamResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for SequencerBlockCommit { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.height != 0 { + len += 1; + } + if !self.block_hash.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimistic.v1alpha1.SequencerBlockCommit", len)?; + if self.height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?; + } + if !self.block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&self.block_hash).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SequencerBlockCommit { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "height", + "block_hash", + "blockHash", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Height, + BlockHash, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "height" => Ok(GeneratedField::Height), + "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SequencerBlockCommit; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimistic.v1alpha1.SequencerBlockCommit") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut height__ = None; + let mut block_hash__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Height => { + if height__.is_some() { + return Err(serde::de::Error::duplicate_field("height")); + } + height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::BlockHash => { + if block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("blockHash")); + } + block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + } + } + Ok(SequencerBlockCommit { + height: height__.unwrap_or_default(), + block_hash: block_hash__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimistic.v1alpha1.SequencerBlockCommit", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs new file mode 100644 index 0000000000..612e5c9fcf --- /dev/null +++ b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs @@ -0,0 +1,516 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBlockCommitmentStreamRequest {} +impl ::prost::Name for GetBlockCommitmentStreamRequest { + const NAME: &'static str = "GetBlockCommitmentStreamRequest"; + const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME + ) + } +} +/// Identifying metadata for blocks that have been successfully committed in the Sequencer. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SequencerBlockCommit { + /// Height of the sequencer block that was committed. + #[prost(uint64, tag = "1")] + pub height: u64, + /// Hash of the sequencer block that was committed. + #[prost(bytes = "bytes", tag = "2")] + pub block_hash: ::prost::bytes::Bytes, +} +impl ::prost::Name for SequencerBlockCommit { + const NAME: &'static str = "SequencerBlockCommit"; + const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBlockCommitmentStreamResponse { + #[prost(message, optional, tag = "1")] + pub commitment: ::core::option::Option, +} +impl ::prost::Name for GetBlockCommitmentStreamResponse { + const NAME: &'static str = "GetBlockCommitmentStreamResponse"; + const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetOptimisticBlockStreamRequest { + /// The rollup id for which the Sequencer block is being streamed. + #[prost(message, optional, tag = "1")] + pub rollup_id: ::core::option::Option, +} +impl ::prost::Name for GetOptimisticBlockStreamRequest { + const NAME: &'static str = "GetOptimisticBlockStreamRequest"; + const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetOptimisticBlockStreamResponse { + /// The optimistic Sequencer block that is being streamed, filtered for the provided rollup id. + #[prost(message, optional, tag = "1")] + pub block: ::core::option::Option, +} +impl ::prost::Name for GetOptimisticBlockStreamResponse { + const NAME: &'static str = "GetOptimisticBlockStreamResponse"; + const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME + ) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod optimistic_block_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// The Sequencer will serve this to the aucitoneer + #[derive(Debug, Clone)] + pub struct OptimisticBlockServiceClient { + inner: tonic::client::Grpc, + } + impl OptimisticBlockServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl OptimisticBlockServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> OptimisticBlockServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + OptimisticBlockServiceClient::new( + InterceptedService::new(inner, interceptor), + ) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided + /// rollup id) to the Auctioneer. + pub async fn get_optimistic_block_stream( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService", + "GetOptimisticBlockStream", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + /// The Sequencer will stream the block commits to the Auctioneer. + pub async fn get_block_commitment_stream( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService", + "GetBlockCommitmentStream", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod optimistic_block_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with OptimisticBlockServiceServer. + #[async_trait] + pub trait OptimisticBlockService: Send + Sync + 'static { + /// Server streaming response type for the GetOptimisticBlockStream method. + type GetOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::GetOptimisticBlockStreamResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided + /// rollup id) to the Auctioneer. + async fn get_optimistic_block_stream( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetBlockCommitmentStream method. + type GetBlockCommitmentStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::GetBlockCommitmentStreamResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// The Sequencer will stream the block commits to the Auctioneer. + async fn get_block_commitment_stream( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// The Sequencer will serve this to the aucitoneer + #[derive(Debug)] + pub struct OptimisticBlockServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl OptimisticBlockServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> + for OptimisticBlockServiceServer + where + T: OptimisticBlockService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream" => { + #[allow(non_camel_case_types)] + struct GetOptimisticBlockStreamSvc( + pub Arc, + ); + impl< + T: OptimisticBlockService, + > tonic::server::ServerStreamingService< + super::GetOptimisticBlockStreamRequest, + > for GetOptimisticBlockStreamSvc { + type Response = super::GetOptimisticBlockStreamResponse; + type ResponseStream = T::GetOptimisticBlockStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GetOptimisticBlockStreamRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_optimistic_block_stream( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetOptimisticBlockStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream" => { + #[allow(non_camel_case_types)] + struct GetBlockCommitmentStreamSvc( + pub Arc, + ); + impl< + T: OptimisticBlockService, + > tonic::server::ServerStreamingService< + super::GetBlockCommitmentStreamRequest, + > for GetBlockCommitmentStreamSvc { + type Response = super::GetBlockCommitmentStreamResponse; + type ResponseStream = T::GetBlockCommitmentStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GetBlockCommitmentStreamRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block_commitment_stream( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockCommitmentStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for OptimisticBlockServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService + for OptimisticBlockServiceServer { + const NAME: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService"; + } +} diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs new file mode 100644 index 0000000000..bc159c576f --- /dev/null +++ b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs @@ -0,0 +1,460 @@ +impl serde::Serialize for GetBlockCommitmentStreamRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetBlockCommitmentStreamRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetBlockCommitmentStreamRequest { + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetBlockCommitmentStreamResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.commitment.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse", len)?; + if let Some(v) = self.commitment.as_ref() { + struct_ser.serialize_field("commitment", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "commitment", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Commitment, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "commitment" => Ok(GeneratedField::Commitment), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetBlockCommitmentStreamResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut commitment__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Commitment => { + if commitment__.is_some() { + return Err(serde::de::Error::duplicate_field("commitment")); + } + commitment__ = map_.next_value()?; + } + } + } + Ok(GetBlockCommitmentStreamResponse { + commitment: commitment__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetOptimisticBlockStreamRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.rollup_id.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest", len)?; + if let Some(v) = self.rollup_id.as_ref() { + struct_ser.serialize_field("rollupId", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "rollup_id", + "rollupId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + RollupId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetOptimisticBlockStreamRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut rollup_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::RollupId => { + if rollup_id__.is_some() { + return Err(serde::de::Error::duplicate_field("rollupId")); + } + rollup_id__ = map_.next_value()?; + } + } + } + Ok(GetOptimisticBlockStreamRequest { + rollup_id: rollup_id__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetOptimisticBlockStreamResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.block.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse", len)?; + if let Some(v) = self.block.as_ref() { + struct_ser.serialize_field("block", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "block", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Block, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "block" => Ok(GeneratedField::Block), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetOptimisticBlockStreamResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut block__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Block => { + if block__.is_some() { + return Err(serde::de::Error::duplicate_field("block")); + } + block__ = map_.next_value()?; + } + } + } + Ok(GetOptimisticBlockStreamResponse { + block: block__, + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for SequencerBlockCommit { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.height != 0 { + len += 1; + } + if !self.block_hash.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit", len)?; + if self.height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?; + } + if !self.block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&self.block_hash).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SequencerBlockCommit { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "height", + "block_hash", + "blockHash", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Height, + BlockHash, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "height" => Ok(GeneratedField::Height), + "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SequencerBlockCommit; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut height__ = None; + let mut block_hash__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Height => { + if height__.is_some() { + return Err(serde::de::Error::duplicate_field("height")); + } + height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::BlockHash => { + if block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("blockHash")); + } + block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + } + } + Ok(SequencerBlockCommit { + height: height__.unwrap_or_default(), + block_hash: block_hash__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit", FIELDS, GeneratedVisitor) + } +} diff --git a/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.rs b/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.rs index 5537a617fb..3607446f91 100644 --- a/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.rs @@ -330,512 +330,6 @@ impl ::prost::Name for SubmittedMetadata { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBlockCommitmentStreamRequest {} -impl ::prost::Name for GetBlockCommitmentStreamRequest { - const NAME: &'static str = "GetBlockCommitmentStreamRequest"; - const PACKAGE: &'static str = "astria.sequencerblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.sequencerblock.v1alpha1.{}", Self::NAME) - } -} -/// Identifying metadata for blocks that have been successfully committed in the Sequencer. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SequencerBlockCommit { - /// Height of the sequencer block that was committed. - #[prost(uint64, tag = "1")] - pub height: u64, - /// Hash of the sequencer block that was committed. - #[prost(bytes = "bytes", tag = "2")] - pub block_hash: ::prost::bytes::Bytes, -} -impl ::prost::Name for SequencerBlockCommit { - const NAME: &'static str = "SequencerBlockCommit"; - const PACKAGE: &'static str = "astria.sequencerblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.sequencerblock.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBlockCommitmentStreamResponse { - #[prost(message, optional, tag = "1")] - pub commitment: ::core::option::Option, -} -impl ::prost::Name for GetBlockCommitmentStreamResponse { - const NAME: &'static str = "GetBlockCommitmentStreamResponse"; - const PACKAGE: &'static str = "astria.sequencerblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.sequencerblock.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetOptimisticBlockStreamRequest { - /// The rollup id for which the Sequencer block is being streamed. - #[prost(message, optional, tag = "1")] - pub rollup_id: ::core::option::Option, -} -impl ::prost::Name for GetOptimisticBlockStreamRequest { - const NAME: &'static str = "GetOptimisticBlockStreamRequest"; - const PACKAGE: &'static str = "astria.sequencerblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.sequencerblock.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetOptimisticBlockStreamResponse { - /// The optimistic Sequencer block that is being streamed, filtered for the provided rollup id. - #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option, -} -impl ::prost::Name for GetOptimisticBlockStreamResponse { - const NAME: &'static str = "GetOptimisticBlockStreamResponse"; - const PACKAGE: &'static str = "astria.sequencerblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.sequencerblock.v1alpha1.{}", Self::NAME) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod optimistic_block_service_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// The Sequencer will serve this to the aucitoneer - #[derive(Debug, Clone)] - pub struct OptimisticBlockServiceClient { - inner: tonic::client::Grpc, - } - impl OptimisticBlockServiceClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl OptimisticBlockServiceClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> OptimisticBlockServiceClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - OptimisticBlockServiceClient::new( - InterceptedService::new(inner, interceptor), - ) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided - /// rollup id) to the Auctioneer. - pub async fn get_optimistic_block_stream( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response< - tonic::codec::Streaming, - >, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.sequencerblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.sequencerblock.v1alpha1.OptimisticBlockService", - "GetOptimisticBlockStream", - ), - ); - self.inner.server_streaming(req, path, codec).await - } - /// The Sequencer will stream the block commits to the Auctioneer. - pub async fn get_block_commitment_stream( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response< - tonic::codec::Streaming, - >, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.sequencerblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.sequencerblock.v1alpha1.OptimisticBlockService", - "GetBlockCommitmentStream", - ), - ); - self.inner.server_streaming(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod optimistic_block_service_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with OptimisticBlockServiceServer. - #[async_trait] - pub trait OptimisticBlockService: Send + Sync + 'static { - /// Server streaming response type for the GetOptimisticBlockStream method. - type GetOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOptimisticBlockStreamResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided - /// rollup id) to the Auctioneer. - async fn get_optimistic_block_stream( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// Server streaming response type for the GetBlockCommitmentStream method. - type GetBlockCommitmentStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetBlockCommitmentStreamResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// The Sequencer will stream the block commits to the Auctioneer. - async fn get_block_commitment_stream( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - /// The Sequencer will serve this to the aucitoneer - #[derive(Debug)] - pub struct OptimisticBlockServiceServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl OptimisticBlockServiceServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> - for OptimisticBlockServiceServer - where - T: OptimisticBlockService, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/astria.sequencerblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream" => { - #[allow(non_camel_case_types)] - struct GetOptimisticBlockStreamSvc( - pub Arc, - ); - impl< - T: OptimisticBlockService, - > tonic::server::ServerStreamingService< - super::GetOptimisticBlockStreamRequest, - > for GetOptimisticBlockStreamSvc { - type Response = super::GetOptimisticBlockStreamResponse; - type ResponseStream = T::GetOptimisticBlockStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::GetOptimisticBlockStreamRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_optimistic_block_stream( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetOptimisticBlockStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/astria.sequencerblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream" => { - #[allow(non_camel_case_types)] - struct GetBlockCommitmentStreamSvc( - pub Arc, - ); - impl< - T: OptimisticBlockService, - > tonic::server::ServerStreamingService< - super::GetBlockCommitmentStreamRequest, - > for GetBlockCommitmentStreamSvc { - type Response = super::GetBlockCommitmentStreamResponse; - type ResponseStream = T::GetBlockCommitmentStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::GetBlockCommitmentStreamRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_block_commitment_stream( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetBlockCommitmentStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for OptimisticBlockServiceServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService - for OptimisticBlockServiceServer { - const NAME: &'static str = "astria.sequencerblock.v1alpha1.OptimisticBlockService"; - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct GetSequencerBlockRequest { /// The height of the block to retrieve. #[prost(uint64, tag = "1")] diff --git a/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.serde.rs index 72c4d3ea26..8b1a35e407 100644 --- a/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.serde.rs +++ b/crates/astria-core/src/generated/astria.sequencerblock.v1alpha1.serde.rs @@ -383,168 +383,6 @@ impl<'de> serde::Deserialize<'de> for FilteredSequencerBlock { deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.FilteredSequencerBlock", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GetBlockCommitmentStreamRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamRequest", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetBlockCommitmentStreamRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(GetBlockCommitmentStreamRequest { - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetBlockCommitmentStreamResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.commitment.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamResponse", len)?; - if let Some(v) = self.commitment.as_ref() { - struct_ser.serialize_field("commitment", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "commitment", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Commitment, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "commitment" => Ok(GeneratedField::Commitment), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetBlockCommitmentStreamResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut commitment__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Commitment => { - if commitment__.is_some() { - return Err(serde::de::Error::duplicate_field("commitment")); - } - commitment__ = map_.next_value()?; - } - } - } - Ok(GetBlockCommitmentStreamResponse { - commitment: commitment__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.GetBlockCommitmentStreamResponse", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for GetFilteredSequencerBlockRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -657,189 +495,6 @@ impl<'de> serde::Deserialize<'de> for GetFilteredSequencerBlockRequest { deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.GetFilteredSequencerBlockRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GetOptimisticBlockStreamRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.rollup_id.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamRequest", len)?; - if let Some(v) = self.rollup_id.as_ref() { - struct_ser.serialize_field("rollupId", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "rollup_id", - "rollupId", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - RollupId, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetOptimisticBlockStreamRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut rollup_id__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::RollupId => { - if rollup_id__.is_some() { - return Err(serde::de::Error::duplicate_field("rollupId")); - } - rollup_id__ = map_.next_value()?; - } - } - } - Ok(GetOptimisticBlockStreamRequest { - rollup_id: rollup_id__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetOptimisticBlockStreamResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.block.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamResponse", len)?; - if let Some(v) = self.block.as_ref() { - struct_ser.serialize_field("block", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "block", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Block, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "block" => Ok(GeneratedField::Block), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetOptimisticBlockStreamResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut block__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Block => { - if block__.is_some() { - return Err(serde::de::Error::duplicate_field("block")); - } - block__ = map_.next_value()?; - } - } - } - Ok(GetOptimisticBlockStreamResponse { - block: block__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.GetOptimisticBlockStreamResponse", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for GetPendingNonceRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1523,121 +1178,6 @@ impl<'de> serde::Deserialize<'de> for SequencerBlock { deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.SequencerBlock", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for SequencerBlockCommit { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.height != 0 { - len += 1; - } - if !self.block_hash.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.v1alpha1.SequencerBlockCommit", len)?; - if self.height != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?; - } - if !self.block_hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&self.block_hash).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for SequencerBlockCommit { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "height", - "block_hash", - "blockHash", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Height, - BlockHash, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "height" => Ok(GeneratedField::Height), - "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = SequencerBlockCommit; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.v1alpha1.SequencerBlockCommit") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut height__ = None; - let mut block_hash__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Height => { - if height__.is_some() { - return Err(serde::de::Error::duplicate_field("height")); - } - height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::BlockHash => { - if block_hash__.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")); - } - block_hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(SequencerBlockCommit { - height: height__.unwrap_or_default(), - block_hash: block_hash__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.v1alpha1.SequencerBlockCommit", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for SequencerBlockHeader { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index a0a39c4d2a..963dd5bbe3 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -165,6 +165,18 @@ pub mod sequencerblock { include!("astria.sequencerblock.v1.serde.rs"); } } + + pub mod optimisticblock { + pub mod v1alpha1 { + include!("astria.sequencerblock.optimisticblock.v1alpha1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.sequencerblock.optimisticblock.v1alpha1.serde.rs"); + } + } + } } #[path = ""] diff --git a/proto/sequencerblockapis/astria/sequencerblock/v1alpha1/optimistic_block.proto b/proto/sequencerblockapis/astria/sequencerblock/optimistic/v1alpha1/service.proto similarity index 88% rename from proto/sequencerblockapis/astria/sequencerblock/v1alpha1/optimistic_block.proto rename to proto/sequencerblockapis/astria/sequencerblock/optimistic/v1alpha1/service.proto index 575643e73b..347a3670cc 100644 --- a/proto/sequencerblockapis/astria/sequencerblock/v1alpha1/optimistic_block.proto +++ b/proto/sequencerblockapis/astria/sequencerblock/optimistic/v1alpha1/service.proto @@ -1,9 +1,9 @@ syntax = "proto3"; -package astria.sequencerblock.v1alpha1; +package astria.sequencerblock.optimistic.v1alpha1; import "astria/primitive/v1/types.proto"; -import "astria/sequencerblock/v1alpha1/block.proto"; +import "astria/sequencerblock/v1/block.proto"; message GetBlockCommitmentStreamRequest {} @@ -26,7 +26,7 @@ message GetOptimisticBlockStreamRequest { message GetOptimisticBlockStreamResponse { // The optimistic Sequencer block that is being streamed, filtered for the provided rollup id. - astria.sequencerblock.v1alpha1.FilteredSequencerBlock block = 1; + astria.sequencerblock.v1.FilteredSequencerBlock block = 1; } // The Sequencer will serve this to the aucitoneer From 942ffc94cb55601f619fa242ed0d40d5e2cd8b6e Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:25:02 +0000 Subject: [PATCH 02/73] fix: update audit ignore list (#1805) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Ignore two new RustSec warnings. ## Background We get two non-critical warnings when running `cargo audit`: [RUSTSEC-2024-0384](https://rustsec.org/advisories/RUSTSEC-2024-0384) and [RUSTSEC-2024-0388](https://rustsec.org/advisories/RUSTSEC-2024-0388).
RUSTSEC-2024-0384

``` Crate: instant Version: 0.1.13 Warning: unmaintained Title: `instant` is unmaintained Date: 2024-09-01 ID: RUSTSEC-2024-0384 URL: https://rustsec.org/advisories/RUSTSEC-2024-0384 Dependency tree: instant 0.1.13 ├── fastrand 1.9.0 │ └── futures-lite 1.13.0 │ ├── isahc 1.7.2 │ │ └── astria-sequencer-relayer 1.0.0 │ └── http-types 2.12.0 │ └── wiremock 0.5.22 │ ├── astria-sequencer-relayer 1.0.0 │ ├── astria-sequencer-client 0.1.0 │ │ ├── astria-sequencer-relayer 1.0.0 │ │ ├── astria-conductor 1.0.0 │ │ ├── astria-composer 1.0.0 │ │ ├── astria-cli 0.5.1 │ │ └── astria-bridge-withdrawer 1.0.1 │ ├── astria-conductor 1.0.0 │ ├── astria-composer 1.0.0 │ └── astria-bridge-withdrawer 1.0.1 ├── ethers-providers 2.0.14 │ ├── ethers-middleware 2.0.14 │ │ └── ethers 2.0.14 │ │ ├── astria-test-utils 0.1.0 │ │ │ └── astria-composer 1.0.0 │ │ ├── astria-composer 1.0.0 │ │ ├── astria-cli 0.5.1 │ │ ├── astria-bridge-withdrawer 1.0.1 │ │ └── astria-bridge-contracts 0.1.0 │ │ ├── astria-cli 0.5.1 │ │ └── astria-bridge-withdrawer 1.0.1 │ ├── ethers-contract 2.0.14 │ │ ├── ethers-middleware 2.0.14 │ │ └── ethers 2.0.14 │ └── ethers 2.0.14 └── ethers-middleware 2.0.14 ```

RUSTSEC-2024-0388

``` Crate: derivative Version: 2.2.0 Warning: unmaintained Title: `derivative` is unmaintained; consider using an alternative Date: 2024-06-26 ID: RUSTSEC-2024-0388 URL: https://rustsec.org/advisories/RUSTSEC-2024-0388 Dependency tree: derivative 2.2.0 ├── penumbra-tct 0.80.7 │ ├── penumbra-txhash 0.80.7 │ │ └── penumbra-ibc 0.80.7 │ │ ├── astria-sequencer 1.0.0 │ │ └── astria-core 0.1.0 │ │ ├── astria-sequencer-utils 0.1.0 │ │ ├── astria-sequencer-relayer 1.0.0 │ │ ├── astria-sequencer-client 0.1.0 │ │ │ ├── astria-sequencer-relayer 1.0.0 │ │ │ ├── astria-conductor 1.0.0 │ │ │ ├── astria-composer 1.0.0 │ │ │ ├── astria-cli 0.5.1 │ │ │ └── astria-bridge-withdrawer 1.0.1 │ │ ├── astria-sequencer 1.0.0 │ │ ├── astria-core 0.1.0 │ │ ├── astria-conductor 1.0.0 │ │ ├── astria-composer 1.0.0 │ │ ├── astria-cli 0.5.1 │ │ ├── astria-bridge-withdrawer 1.0.1 │ │ └── astria-bridge-contracts 0.1.0 │ │ ├── astria-cli 0.5.1 │ │ └── astria-bridge-withdrawer 1.0.1 │ ├── penumbra-sct 0.80.7 │ │ └── penumbra-ibc 0.80.7 │ └── penumbra-keys 0.80.7 │ └── penumbra-sct 0.80.7 ├── penumbra-num 0.80.7 │ ├── penumbra-ibc 0.80.7 │ └── penumbra-asset 0.80.7 │ ├── penumbra-keys 0.80.7 │ └── penumbra-ibc 0.80.7 ├── penumbra-keys 0.80.7 ├── penumbra-asset 0.80.7 ├── ark-r1cs-std 0.4.0 │ ├── poseidon377 1.2.0 │ │ ├── penumbra-tct 0.80.7 │ │ ├── penumbra-sct 0.80.7 │ │ ├── penumbra-keys 0.80.7 │ │ └── penumbra-asset 0.80.7 │ ├── poseidon-permutation 1.1.0 │ │ └── poseidon377 1.2.0 │ ├── penumbra-tct 0.80.7 │ ├── penumbra-sct 0.80.7 │ ├── penumbra-num 0.80.7 │ ├── penumbra-keys 0.80.7 │ ├── penumbra-asset 0.80.7 │ └── decaf377 0.10.1 │ ├── poseidon377 1.2.0 │ ├── poseidon-permutation 1.1.0 │ ├── poseidon-parameters 1.1.0 │ │ ├── poseidon377 1.2.0 │ │ └── poseidon-permutation 1.1.0 │ ├── penumbra-tct 0.80.7 │ ├── penumbra-sct 0.80.7 │ ├── penumbra-num 0.80.7 │ ├── penumbra-keys 0.80.7 │ ├── penumbra-asset 0.80.7 │ ├── decaf377-rdsa 0.11.0 │ │ ├── penumbra-sct 0.80.7 │ │ ├── penumbra-proto 0.80.7 │ │ │ ├── penumbra-txhash 0.80.7 │ │ │ ├── penumbra-tct 0.80.7 │ │ │ ├── penumbra-sct 0.80.7 │ │ │ ├── penumbra-num 0.80.7 │ │ │ ├── penumbra-keys 0.80.7 │ │ │ ├── penumbra-ibc 0.80.7 │ │ │ ├── penumbra-asset 0.80.7 │ │ │ ├── astria-sequencer 1.0.0 │ │ │ └── astria-core 0.1.0 │ │ ├── penumbra-num 0.80.7 │ │ ├── penumbra-keys 0.80.7 │ │ └── penumbra-asset 0.80.7 │ ├── decaf377-ka 0.80.7 │ │ └── penumbra-keys 0.80.7 │ └── decaf377-fmd 0.80.7 │ ├── penumbra-proto 0.80.7 │ ├── penumbra-num 0.80.7 │ ├── penumbra-keys 0.80.7 │ └── penumbra-asset 0.80.7 ├── ark-poly 0.4.2 │ ├── ark-groth16 0.4.0 │ │ ├── poseidon377 1.2.0 │ │ ├── penumbra-num 0.80.7 │ │ └── decaf377 0.10.1 │ └── ark-ec 0.4.2 │ ├── poseidon377 1.2.0 │ ├── decaf377 0.10.1 │ ├── ark-r1cs-std 0.4.0 │ ├── ark-groth16 0.4.0 │ ├── ark-ed-on-bls12-377 0.4.0 │ │ ├── penumbra-tct 0.80.7 │ │ └── decaf377 0.10.1 │ ├── ark-crypto-primitives 0.4.0 │ │ └── ark-groth16 0.4.0 │ └── ark-bls12-377 0.4.0 │ ├── decaf377 0.10.1 │ └── ark-ed-on-bls12-377 0.4.0 ├── ark-ff 0.4.2 │ ├── ruint 1.12.3 │ │ └── celestia-types 0.1.1 │ │ ├── celestia-rpc 0.1.1 │ │ │ └── astria-conductor 1.0.0 │ │ ├── astria-sequencer-relayer 1.0.0 │ │ ├── astria-core 0.1.0 │ │ └── astria-conductor 1.0.0 │ ├── poseidon377 1.2.0 │ ├── poseidon-permutation 1.1.0 │ ├── penumbra-tct 0.80.7 │ ├── penumbra-sct 0.80.7 │ ├── penumbra-num 0.80.7 │ ├── penumbra-keys 0.80.7 │ ├── penumbra-ibc 0.80.7 │ ├── penumbra-asset 0.80.7 │ ├── decaf377-rdsa 0.11.0 │ ├── decaf377-ka 0.80.7 │ ├── decaf377-fmd 0.80.7 │ ├── decaf377 0.10.1 │ ├── ark-snark 0.4.0 │ │ ├── poseidon377 1.2.0 │ │ ├── penumbra-num 0.80.7 │ │ ├── decaf377 0.10.1 │ │ └── ark-crypto-primitives 0.4.0 │ ├── ark-relations 0.4.0 │ │ ├── poseidon377 1.2.0 │ │ ├── poseidon-permutation 1.1.0 │ │ ├── penumbra-tct 0.80.7 │ │ ├── penumbra-sct 0.80.7 │ │ ├── penumbra-num 0.80.7 │ │ ├── penumbra-keys 0.80.7 │ │ ├── penumbra-asset 0.80.7 │ │ ├── decaf377 0.10.1 │ │ ├── ark-snark 0.4.0 │ │ ├── ark-r1cs-std 0.4.0 │ │ ├── ark-groth16 0.4.0 │ │ └── ark-crypto-primitives 0.4.0 │ ├── ark-r1cs-std 0.4.0 │ ├── ark-poly 0.4.2 │ ├── ark-groth16 0.4.0 │ ├── ark-ed-on-bls12-377 0.4.0 │ ├── ark-ec 0.4.2 │ ├── ark-crypto-primitives 0.4.0 │ └── ark-bls12-377 0.4.0 ├── ark-ff 0.3.0 │ └── ruint 1.12.3 ├── ark-ec 0.4.2 └── ark-crypto-primitives 0.4.0 ```

Given that the RustSec report doesn't suggest any concrete problems with either crate, and how difficult it will be to move away from these dependencies, I have just ignored these warnings in CI. ## Changes - Ignore RustSec warnings in `.cargo/audit.toml`. ## Testing Ran `cargo audit` locally. ## Changelogs No updates required - nothing really fixed or updated. ## Related Issues Closes #1796. Closes #1797. --- .cargo/audit.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index bfe280fdd3..a24651942e 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -6,4 +6,10 @@ ignore = [ # `proc-macro-error` is Unmaintained. It is a transient dependency of borsh crates, so cannot # easily be replaced. "RUSTSEC-2024-0370", + # `instant` is Unmaintained. It is a transient dependency of `isahc`, `wiremock` and `ethers`, so + # cannot easily be replaced. + "RUSTSEC-2024-0384", + # `derivative` is Unmaintained. It is a transient dependency of many crates including several + # penumbra ones, so cannot easily be replaced. + "RUSTSEC-2024-0388", ] From 9b6ecc7f3d06a59955e512a74e9b434292eab1dd Mon Sep 17 00:00:00 2001 From: noot <36753753+noot@users.noreply.github.com> Date: Tue, 19 Nov 2024 06:35:10 -0500 Subject: [PATCH 03/73] chore(docs): updates to data verification and inclusion proof specs (#1689) ## Summary minor updates to the data verification and inclusion proof specs, mostly renaming/updating the types and some clarifications. ## Changes - update [specs/data-flow-and-verification.md](https://github.com/astriaorg/astria/compare/noot/specs?expand=1#diff-ec3cc86eef70a0285e3cd7fbc902eeb2ef082783cda5916b3d9b81f80e0b4917) - update [specs/sequencer-inclusion-proofs.md](https://github.com/astriaorg/astria/compare/noot/specs?expand=1#diff-0b52370ce97078002b7f4682377d6847fad36814fea97e7eab930e1edc9270ec) --- specs/data-flow-and-verification.md | 197 ++++++++++++++++------------ specs/sequencer-inclusion-proofs.md | 20 +-- 2 files changed, 122 insertions(+), 95 deletions(-) diff --git a/specs/data-flow-and-verification.md b/specs/data-flow-and-verification.md index 613b00f70a..6bde2222d9 100644 --- a/specs/data-flow-and-verification.md +++ b/specs/data-flow-and-verification.md @@ -11,32 +11,33 @@ the exit point is execution by a rollup node. ## Entry point -The entry point for rollup data is via a `sequence::Action`, which can become -part of a sequencer transaction. The data types are as follows: +The entry point for rollup data is via a `RollupDataSubmission` action, which can +become part of a sequencer transaction. The data types are as follows: ```rust -// sequence::Action -pub struct Action { - pub(crate) chain_id: Vec, - pub(crate) data: Vec, +pub struct RollupDataSubmission { + rollup_id: RollupId, + data: Bytes, + // the asset to pay fees with + fee_asset: asset::Denom, } ``` ```rust -// an unsigned transaction -pub struct Unsigned { - pub(crate) nonce: Nonce, - pub(crate) actions: Vec, +// an unsigned transaction body +pub struct TransactionBody { + actions: Actions, // vector of actions + params: TransactionParams, // chain id, nonce } ``` -The `data` field inside the `sequence::Action` is arbitrary bytes, which should +The `data` field inside the `RollupDataSubmission` is arbitrary bytes, which should be an encoded rollup transaction. The sequencer is agnostic to the transaction -format of the rollups using it. The `chain_id` field is an identifier for the +format of the rollups using it. The `rollup_id` field is an identifier for the rollup the data is destined for. To submit rollup data to the system, the user creates a transaction with a -`sequence::Action` within it and signs and submits it to the sequencer. The +`RollupDataSubmission` within it and signs and submits it to the sequencer. The sequencer will then include it in a block, thus finalizing its ordering. ## Sequencer to data availability @@ -47,56 +48,81 @@ the block data is published via a data availability layer. The block data published is as follows: ```rust -pub struct SequencerBlockData { - block_hash: Hash, - header: Header, - /// chain ID -> rollup transactions - rollup_data: BTreeMap>>, - /// The root of the action tree for this block. - action_tree_root: [u8; 32], - /// The inclusion proof that the action tree root is included - /// in `Header::data_hash`. - action_tree_root_inclusion_proof: merkle::Proof, - /// The commitment to the chain IDs of the rollup data. - /// The merkle root of the tree where the leaves are the chain IDs. - chain_ids_commitment: [u8; 32], - /// The inclusion proof that the chain IDs commitment is included - /// in `Header::data_hash`. - chain_ids_commitment_inclusion_proof: merkle::Proof, +pub struct SequencerBlockHeader { + chain_id: tendermint::chain::Id, + height: tendermint::block::Height, + time: Time, + // the merkle root of all the rollup data in the block + rollup_transactions_root: [u8; 32], + // the merkle root of all transactions in the block + data_hash: [u8; 32], + proposer_address: account::Id, +} + +pub struct SequencerBlock { + /// the cometbft block hash for this block + block_hash: [u8; 32], + /// the block header, which contains cometbft header info and additional sequencer-specific + /// commitments. + header: SequencerBlockHeader, + /// The collection of rollup transactions that were included in this block. + rollup_transactions: IndexMap, + /// The inclusion proof that the rollup transactions merkle root is included + /// in `header.data_hash`. + rollup_transactions_proof: merkle::Proof, + /// The inclusion proof that the rollup IDs commitment is included + /// in `header.data_hash`. + rollup_ids_proof: merkle::Proof, } ``` -When this data is actually published, it's split into multiple structures. +When the `SequencerBlock` is actually published, it's split into multiple structures. Specifically, the data for each rollup is written independently, while a "base" -data type which contains the rollup chain IDs included in the block is also -written. This allows each rollup to only require the `SequencerNamespaceData` -for the block and the `RollupNamespaceData` for its own rollup transactions. For -each block, if there are N rollup chain IDs included, 1 + N structures are -written to DA. +data type which contains all the other `SequencerBlock` info, plus the list of +rollup IDs in the block, is written. This allows each rollup to only require +the `SequencerNamespaceData` for the block and the `RollupNamespaceData` for +its own rollup transactions. For each block, if there are N rollup chain IDs +included, 1 + N structures are written to DA. ```rust -/// SequencerNamespaceData represents the data written to the "base" -/// sequencer namespace. It contains all the other chain IDs (and thus, +/// SubmittedMetadata represents the data written to the "base" +/// sequencer namespace. It contains all the other rollup IDs (and thus, /// namespaces) that were also written to in the same block. -#[derive(Serialize, Deserialize, Debug)] -pub struct SequencerNamespaceData { - pub block_hash: Hash, - pub header: Header, - pub rollup_chain_ids: Vec, - pub action_tree_root: [u8; 32], - pub action_tree_root_inclusion_proof: InclusionProof, - pub chain_ids_commitment: [u8; 32], +pub struct SubmittedMetadata { + /// The block hash obtained from hashing `.header`. + block_hash: [u8; 32], + /// The sequencer block header. + header: SequencerBlockHeader, + /// The rollup IDs for which `SubmittedRollupData`s were submitted to celestia. + /// Corresponds to the `astria.sequencer.v1.RollupTransactions.id` field + /// and is extracted from `astria.SequencerBlock.rollup_transactions`. + rollup_ids: Vec, + /// The proof that the rollup transactions are included in sequencer block. + /// Corresponds to `astria.SequencerBlock.rollup_transactions_proof`. + rollup_transactions_proof: merkle::Proof, + /// The proof that this sequencer blob includes all rollup IDs of + /// the original sequencer block it was derived from. + /// This proof together with `Sha256(MTH(rollup_ids))` (Sha256 + /// applied to the Merkle Tree Hash of the rollup ID sequence) must be + /// equal to `header.data_hash` which itself must match + /// `astria.SequencerBlock.header.data_hash`. This field corresponds to + /// `astria.SequencerBlock.rollup_ids_proof`. + rollup_ids_proof: merkle::Proof, } ``` ```rust -/// RollupNamespaceData represents the data written to a rollup namespace. -#[derive(Serialize, Deserialize, Debug)] -pub struct RollupNamespaceData { - pub(crate) block_hash: Hash, - pub(crate) chain_id: ChainId, - pub rollup_txs: Vec>, - pub(crate) inclusion_proof: InclusionProof, +/// SubmittedRollupData represents the data written to a rollup namespace. +pub struct SubmittedRollupData { + /// The hash of the sequencer block. Must be 32 bytes. + sequencer_block_hash: [u8; 32], + /// The 32 bytes identifying the rollup this blob belongs to. Matches + /// `astria.sequencerblock.v1.RollupTransactions.rollup_id` + rollup_id: RollupId, + /// A list of opaque bytes that are serialized rollup transactions. + transactions: Vec, + /// The proof that these rollup transactions are included in sequencer block. + proof: merkle::Proof, } ``` @@ -108,9 +134,6 @@ properties as ordering, completeness, and correctness respectively. It is able to do this *without* requiring the full transaction data of the block, as is explained below. -Note that the `Header` field in `SequencerNamespaceData` is a [Tendermint -header](https://github.com/informalsystems/tendermint-rs/blob/4d81b67c28510db7d2d99ed62ebfa9fdf0e02141/tendermint/src/block/header.rs#L25). - ## Data availability to rollup node For a rollup node to verify the ordering, completeness, and correctness of the @@ -120,13 +143,13 @@ block data it receives, it must verify the following: 2. the block hash was in fact committed by the sequencer (ie. >2/3 stake voted to commit this block hash to the chain) 3. the block header correctly hashes to the block hash -4. the `data_hash` inside the header contains the `action_tree_root` of the +4. the `data_hash` inside the header contains the `rollup_transactions_root` of the block (see [sequencer inclusion proofs](sequencer-inclusion-proofs.md) for - details), which is a commitment to the `sequence:Action`s in the block -5. the `rollup_txs` inside `RollupNamespaceData` is contained within the - `action_tree_root` -6. the `chain_ids_commitment` is a valid commitment to `rollup_chain_ids` -7. the `data_hash` inside the header contains the `chain_ids_commitment` + details), which is a commitment to the `RollupDataSubmission`s in the block +5. the `transactions` inside `SubmittedRollupData` is contained within the + `rollup_transactions_root` +6. the `rollup_ids_commitment` is a valid commitment to `rollup_ids` +7. the `data_hash` inside the header contains the `rollup_ids_commitment` for the block. Let's go through these one-by-one. @@ -159,45 +182,45 @@ The block hash is a commitment to the block header (specifically, the merkle root of the tree where the leaves are each header field). We then verify that the block header merkleizes to the block hash correctly. -### 4. `action_tree_root` +### 4. `rollup_transactions_root` -The block's data (transactions) contain the `action_tree_root` of the block (see -[sequencer inclusion proofs](sequencer-inclusion-proofs.md) for details), which -is a commitment to the `sequence:Action`s in the block. Specifically, the -`action_tree_root` is the root of a merkle tree where each leaf is a commitment -to the rollup data for one spceific rollup. The block header contains the field -`data_hash` which is the merkle root of all the transactions in a block. Since -`action_tree_root` is a transaction, we can prove its inclusion inside -`data_hash` (the `action_tree_root_inclusion_proof` field inside -`SequencerNamespaceData`). Then, in the next step, we can verify that the rollup -data we received was included inside `action_tree_root`. +The block's data (transactions) contain the `rollup_transactions_root` of the +block (see [sequencer inclusion proofs](sequencer-inclusion-proofs.md) for details), +which is a commitment to the `RollupDataSubmission`s in the block. Specifically, +the `rollup_transactions_root` is the root of a merkle tree where each leaf is a + commitment to the rollup data for one spceific rollup. The block header contains +the field `data_hash` which is the merkle root of all the transactions in a block. +Since `rollup_transactions_root` is a transaction, we can prove its inclusion inside +`data_hash` (the `rollup_transactions_proof` field inside +`SubmittedMetadata`). Then, in the next step, we can verify that the rollup +data we received was included inside `rollup_transactions_root`. ### 5. `rollup_txs` We calculate a commitment of the rollup data we receive (`rollup_txs` inside -`RollupNamespaceData`). We then verify that this data is included inside -`action_tree_root` (via the `inclusion_proof` field inside -`RollupNamespaceData`). At this point, we are now certain that the rollup data +`SubmittedRollupMetadata`). We then verify that this data is included inside +`rollup_transactions_root` (via the `proof` field inside +`SubmittedRollupMetadata`). At this point, we are now certain that the rollup data we received, which is a subset of the entire block's data, was in fact committed by the majority of the sequencer chain's validators. -### 6. `chain_ids_commitment` +### 6. `rollup_ids_root` -The `SequencerNamespaceData` contains a list of the `rollup_chain_ids` that were -included in the block. However, to ensure that chain IDs are not omitted when +The `SubmittedMetadata` contains a list of the `rollup_ids` that were +included in the block. However, to ensure that rollup IDs are not omitted when publishing the data (which would be undetectable to rollup nodes without forcing -them to pull the entire block's data), we also add a commitment to the chain IDs +them to pull the entire block's data), we also add a commitment to the rollup IDs in the block inside the block's transaction data. We ensure that the -`rollup_chain_ids` inside `SequencerNamespaceData` match the -`chain_ids_commitment`. This proves that no chain IDs were omitted from the -published block, as if any were omitted, then the `chain_ids_commitment` would -not match the commitment generated from `rollup_chain_ids`. +`rollup_ids` inside `SubmittedMetadata` match the +`rollup_ids_root`. This proves that no rollup IDs were omitted from the +published block, as if any were omitted, then the `rollup_ids_root` would +not match the commitment generated from `rollup_ids`. -### 7. `chain_ids_commitment_inclusion_proof` +### 7. `rollup_ids_root_inclusion_proof` -Similarly to verification of `action_tree_root` inside `data_hash`, we also verify -an inclusion proof of `chain_ids_commitment` inside `data_hash` when receiving a -published block. +Similarly to verification of `rollup_transactions_root` inside `data_hash`, we also +verify an inclusion proof of `rollup_ids_root` inside `data_hash` when receiving +a published block. ## Exit point diff --git a/specs/sequencer-inclusion-proofs.md b/specs/sequencer-inclusion-proofs.md index 273d2d028d..51b1f99417 100644 --- a/specs/sequencer-inclusion-proofs.md +++ b/specs/sequencer-inclusion-proofs.md @@ -29,14 +29,18 @@ A rollup conductor needs to be able to verify its subset of relevant data without needing to pull all the transaction data for a block. To do this, the block proposer includes a special "commitment tx" at the start of the block. This a special transaction type that can only be included by the proposer. +The "commitment tx" is the merkle root of a tree where each leaf is a commitment +to the batch of transactions for one specific rollup. ![image](assets/sequencer_inclusion_proof_0.png) When building a block, the proposer deconstructs all txs into their contained -`sequence::Action`s and groups them all. Remember that 1 `sequence::Action` -corresponds to 1 rollup transaction. Then, a commitment to the set of all -actions for a chain becomes a leaf in a merkle tree, the root of which becomes -the "commitment tx" +`RollupDataSubmission` actions and groups them all. Remember that 1 +`RollupDataSubmission` action corresponds to 1 rollup transaction. Then, a +commitment to the set of all actions for a chain becomes a leaf in a merkle tree, +the root of which becomes the "commitment tx". The other validators reconstruct +this merkle tree when validating the proposal, and only accept the proposal if +the tree is a valid representation of the rollup data in the block. ![image](assets/sequencer_inclusion_proof_2.png) @@ -55,8 +59,8 @@ For example: layer and calculate the commitment which should match the leaf in red (in the above diagram) - we use the inclusion proof (in green) to verify the txs for chain C were - included in the action tree -- we verify a proof of inclusion of "commitment tx" (action tree root) inside + included in the rollup data merkle tree +- we verify a proof of inclusion of "commitment tx" (rollup data tree root) inside the block header's `data_hash` - we verify that `data_hash` was correctly included in the block's `block_hash` - verify `block_hash`'s cometbft >2/3 commitment @@ -68,8 +72,8 @@ staking power of the sequencer chain. Additionally, the commitment to the actions for a chain actually also includes a merkle root. The commitment contains of the merkle root of a tree where where -the leaves are the transactions for that rollup; ie. all the `sequence::Action`s -for that chain. "Commitment to actions for chain X" is implemented as `(chain_id +the leaves are the transactions for that rollup; ie. all the `RollupDataSubmission` +actions for that chain. "Commitment to actions for chain X" is implemented as `(chain_id || root of tx tree for rollup)`, allowing for easy verification that a specific rollup transaction was included in a sequencer block. This isn't required for any specific conductor logic, but nice for applications building on top of the From 3830a3d3958591cb16b9f3b8cfc1efa560c0e0ee Mon Sep 17 00:00:00 2001 From: quasystaty Date: Tue, 19 Nov 2024 13:43:06 +0200 Subject: [PATCH 04/73] fix(ci): remove unnecessary checks from ibc-tests (#1813) ## Summary removes immediate celestia balance check after performing ibc-transfer **from** celestia ## Background Current ibc-tests checks celestia wallet balance immediately after permorming an ibc transfer, this is redundant as we check the bablance change on the sequencer side. In addition to currently being broken duo to outdated expected value, the test currently passes randomly, most likely duo to latency. ## Changes - remove celestia balance checks from ibc transfer from celestia - moves ibc-transfer check in `run-without-native` above fee-asset change, avoiding latency issues. ## Testing - locally tested bad behavior by adding a 1s sleep prior to the first balance check ## Changelogs No updates required. --- charts/ibc-test.just | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/charts/ibc-test.just b/charts/ibc-test.just index 0e330f8da1..6b8e472328 100644 --- a/charts/ibc-test.just +++ b/charts/ibc-test.just @@ -89,10 +89,7 @@ run-without-native tag=defaultTag: # Execute the transfer from Celestia to the Rollup just ibc-test _do-ibc-transfer {{defaultNamespace}} {{sequencer_sudo_address}} - # Add transfer/channel-0/utia as fee-asset - docker run --rm --network host $ASTRIA_CLI_IMAGE sequencer sudo fee-asset add --private-key {{sequencer_sudo_pkey}} --asset transfer/channel-0/utia --sequencer-url {{sequencer_rpc_url}} --sequencer.chain-id {{sequencer_chain_id}} # check that sequencer balance updated correctly - EXPECTED_BALANCE=$(echo "1 * {{transfer_amount}}" | bc) for i in {1..50} do @@ -110,6 +107,11 @@ run-without-native tag=defaultTag: exit 1 fi + # Add transfer/channel-0/utia as allowed fee-asset + docker run --rm --network host $ASTRIA_CLI_IMAGE sequencer sudo fee-asset add --private-key {{sequencer_sudo_pkey}} --asset transfer/channel-0/utia --sequencer-url {{sequencer_rpc_url}} --sequencer.chain-id {{sequencer_chain_id}} + + # TODO: query allowd fee asset verifying succefull addition + [no-cd] run tag=defaultTag: #!/usr/bin/env bash @@ -149,23 +151,6 @@ run tag=defaultTag: # Execute the transfer from Celstia to sequencer with compat address just ibc-test _do-ibc-transfer {{defaultNamespace}} {{compat_address}} - # check that celestia balance updated correctly - for i in {1..50} - do - current_celestia_balance=$(just ibc-test get-celestia-balance) - echo "check $i, balance: $current_celestia_balance, expected: $expected_celestia_balance" - if (( $expected_celestia_balance == $current_celestia_balance )); then - expected_celestia_balance_found="1" - break - else - sleep 1 - fi - done - if [[ -z $expected_celestia_balance_found ]]; then - echo "expected celestia balance was not found after withdraw; IBC transfer from Celestia to the Rollup failed" - exit 1 - fi - # check that sequencer balance updated correctly ASTRIA_CLI_IMAGE="{{cli_image}}{{ if tag != '' { replace(':#', '#', tag) } else { '' } }}" EXPECTED_BALANCE=$(echo "1 * {{transfer_amount}}" | bc) @@ -283,23 +268,6 @@ run-timeout tag=defaultTag: # Execute the transfer from Celstia to sequencer with compat address just ibc-test _do-ibc-transfer {{defaultNamespace}} {{compat_address}} - # check that celestia balance updated correctly - for i in {1..50} - do - current_celestia_balance=$(just ibc-test get-celestia-balance) - echo "check $i, balance: $current_celestia_balance, expected: $expected_celestia_balance" - if (( $expected_celestia_balance == $current_celestia_balance )); then - expected_celestia_balance_found="1" - break - else - sleep 1 - fi - done - if [[ -z $expected_celestia_balance_found ]]; then - echo "expected celestia balance was not found after withdraw; IBC transfer from Celestia to the Rollup failed" - exit 1 - fi - # check that sequencer balance updated correctly ASTRIA_CLI_IMAGE="{{cli_image}}{{ if tag != '' { replace(':#', '#', tag) } else { '' } }}" EXPECTED_BALANCE=$(echo "1 * {{transfer_amount}}" | bc) From 471ee91197085047243a9cd2d90379254f216738 Mon Sep 17 00:00:00 2001 From: quasystaty Date: Tue, 19 Nov 2024 14:07:04 +0200 Subject: [PATCH 05/73] feat(cli): allowed fee-assets query (#1816) ## Summary Adds `astria-cli sequencer fee-assets get` query command, getting all allowed fee-assets ## Background The astria CLI should have a query command for all abci query paths, the PR is part of an effort to accomplish this. It also enables more robust smoke-tests. ## Changes - Adds allowed fee assets query subcommand. ## Testing Manually runs the command against local, Dawn and mainnet. ## Changelogs Changelogs updated. ## Related Issues Link any issues that are related, prefer full GitHub links. closes #1815 --- crates/astria-cli/CHANGELOG.md | 4 ++ crates/astria-cli/src/sequencer/fee_assets.rs | 58 +++++++++++++++++++ crates/astria-cli/src/sequencer/mod.rs | 4 ++ 3 files changed, 66 insertions(+) create mode 100644 crates/astria-cli/src/sequencer/fee_assets.rs diff --git a/crates/astria-cli/CHANGELOG.md b/crates/astria-cli/CHANGELOG.md index 09bc9674b2..738181bef5 100644 --- a/crates/astria-cli/CHANGELOG.md +++ b/crates/astria-cli/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `fee-assets` subcommand to `sequencer` CLI [#1816](https://github.com/astriaorg/astria/pull/1816). + ### Fixed - Fixed ICS20 withdrawal source when using channel with more than one diff --git a/crates/astria-cli/src/sequencer/fee_assets.rs b/crates/astria-cli/src/sequencer/fee_assets.rs new file mode 100644 index 0000000000..b5fa567e5c --- /dev/null +++ b/crates/astria-cli/src/sequencer/fee_assets.rs @@ -0,0 +1,58 @@ +use astria_sequencer_client::{ + HttpClient, + SequencerClientExt as _, +}; +use clap::Subcommand; +use color_eyre::eyre::{ + self, + WrapErr as _, +}; + +#[derive(Debug, clap::Args)] +pub(super) struct Command { + #[command(subcommand)] + command: SubCommand, +} + +impl Command { + pub(super) async fn run(self) -> eyre::Result<()> { + let SubCommand::Get(get) = self.command; + get.run().await + } +} + +#[derive(Debug, Subcommand)] +enum SubCommand { + /// Get the balance of a Sequencer account + Get(Get), +} + +#[derive(clap::Args, Debug)] +struct Get { + /// The url of the Sequencer node + #[arg( + long, + env = "SEQUENCER_URL", + default_value = crate::DEFAULT_SEQUENCER_RPC + )] + sequencer_url: String, +} + +impl Get { + async fn run(self) -> eyre::Result<()> { + let sequencer_client = HttpClient::new(self.sequencer_url.as_str()) + .wrap_err("failed constructing http sequencer client")?; + + let res = sequencer_client + .get_allowed_fee_assets() + .await + .wrap_err("failed to get fee assets")?; + + println!("Allowed fee assets:"); + for asset in res.fee_assets { + println!(" {asset}"); + } + + Ok(()) + } +} diff --git a/crates/astria-cli/src/sequencer/mod.rs b/crates/astria-cli/src/sequencer/mod.rs index 6beda8d5ec..d8fa0a7233 100644 --- a/crates/astria-cli/src/sequencer/mod.rs +++ b/crates/astria-cli/src/sequencer/mod.rs @@ -8,6 +8,7 @@ mod block_height; mod bridge_account; mod bridge_lock; mod bridge_sudo_change; +mod fee_assets; mod ics20_withdrawal; mod init_bridge_account; mod sign; @@ -39,6 +40,7 @@ impl Command { SubCommand::Sign(sign) => sign.run(), SubCommand::BridgeSudoChange(bridge_sudo_change) => bridge_sudo_change.run().await, SubCommand::BridgeAccount(bridge_account) => bridge_account.run().await, + SubCommand::FeeAssets(fee_assets) => fee_assets.run().await, } } } @@ -80,4 +82,6 @@ enum SubCommand { BridgeSudoChange(bridge_sudo_change::Command), /// Commands for interacting with the bridge account BridgeAccount(bridge_account::Command), + /// Command for interacting with allowed fee assets + FeeAssets(fee_assets::Command), } From 66704f2e054c9368833452541f0d98c0ab12e03a Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 19 Nov 2024 17:51:09 +0100 Subject: [PATCH 06/73] fix(ci, core): delete unused generated code, generate overlooked code --- .../src/bridge_withdrawer/mod.rs | 2 +- .../src/bridge_withdrawer/startup.rs | 19 +- .../bridge_withdrawer/submitter/builder.rs | 2 +- .../src/bridge_withdrawer/submitter/mod.rs | 2 +- .../tests/blackbox/helpers/mock_cometbft.rs | 2 +- .../tests/blackbox/helpers/mock_sequencer.rs | 2 +- .../src/sequencer/threshold/sign.rs | 2 +- .../src/sequencer/threshold/verify.rs | 2 +- crates/astria-composer/src/collectors/grpc.rs | 2 +- .../astria-composer/src/executor/builder.rs | 2 +- crates/astria-composer/src/executor/mod.rs | 2 +- crates/astria-composer/src/grpc.rs | 2 +- .../tests/blackbox/grpc_collector.rs | 2 +- .../blackbox/helper/mock_grpc_sequencer.rs | 2 +- .../tests/blackbox/helper/mod.rs | 2 +- .../src/celestia/block_verifier.rs | 2 +- .../astria-conductor/src/celestia/convert.rs | 2 +- .../astria-conductor/src/executor/client.rs | 2 +- crates/astria-conductor/src/executor/state.rs | 2 +- crates/astria-conductor/src/executor/tests.rs | 2 +- .../astria-conductor/src/sequencer/client.rs | 2 +- .../tests/blackbox/firm_only.rs | 2 +- .../tests/blackbox/helpers/macros.rs | 14 +- .../tests/blackbox/helpers/mock_grpc.rs | 2 +- .../tests/blackbox/helpers/mod.rs | 12 +- .../tests/blackbox/soft_only.rs | 2 +- crates/astria-core/CHANGELOG.md | 6 + crates/astria-core/src/execution/v1/mod.rs | 8 +- .../astria.protocol.transaction.v1.rs | 2 +- .../astria.protocol.transaction.v1alpha1.rs | 2 +- ...sequencerblock.optimisticblock.v1alpha1.rs | 516 ------------------ ...cerblock.optimisticblock.v1alpha1.serde.rs | 460 ---------------- crates/astria-core/src/generated/mod.rs | 206 +++---- crates/astria-core/src/primitive/v1/mod.rs | 2 +- crates/astria-core/src/primitive/v1/u128.rs | 5 +- .../astria-core/src/protocol/account/mod.rs | 2 - .../src/protocol/account/v1/mod.rs | 10 +- crates/astria-core/src/protocol/asset/mod.rs | 2 - .../astria-core/src/protocol/asset/v1/mod.rs | 12 +- crates/astria-core/src/protocol/bridge/mod.rs | 2 +- crates/astria-core/src/protocol/fees/v1.rs | 2 +- crates/astria-core/src/protocol/genesis/v1.rs | 2 +- crates/astria-core/src/protocol/memos/v1.rs | 2 +- .../src/protocol/transaction/v1/mod.rs | 2 +- .../src/sequencerblock/v1/block.rs | 7 +- .../astria-core/src/sequencerblock/v1/mod.rs | 2 +- .../src/extension_trait.rs | 24 +- .../astria-sequencer-client/src/tests/http.rs | 12 +- .../src/relayer/builder.rs | 2 +- .../src/relayer/mod.rs | 2 +- .../src/relayer/read.rs | 2 +- .../src/relayer/write/conversion.rs | 2 +- .../blackbox/helpers/mock_sequencer_server.rs | 2 +- .../astria-sequencer-utils/src/blob_parser.rs | 2 +- .../src/genesis_example.rs | 6 +- .../src/app/benchmark_and_test_utils.rs | 8 +- crates/astria-sequencer/src/app/benchmarks.rs | 2 +- crates/astria-sequencer/src/app/mod.rs | 2 +- .../astria-sequencer/src/app/tests_app/mod.rs | 2 +- .../src/app/tests_breaking_changes.rs | 2 +- .../src/app/tests_execute_transaction.rs | 4 +- crates/astria-sequencer/src/bridge/query.rs | 2 +- crates/astria-sequencer/src/fees/query.rs | 2 +- crates/astria-sequencer/src/fees/tests.rs | 2 +- crates/astria-sequencer/src/grpc/sequencer.rs | 2 +- crates/astria-sequencer/src/sequencer.rs | 2 +- .../astria-sequencer/src/service/consensus.rs | 16 +- .../astria-sequencer/src/service/info/mod.rs | 6 +- .../src/service/mempool/mod.rs | 2 +- tools/protobuf-compiler/CHANGELOG.md | 15 + tools/protobuf-compiler/src/main.rs | 43 +- 71 files changed, 291 insertions(+), 1218 deletions(-) delete mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs delete mode 100644 crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs create mode 100644 tools/protobuf-compiler/CHANGELOG.md diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/mod.rs index b1aa923f72..aeb0f1a721 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/mod.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use astria_core::generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; +use astria_core::generated::astria::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; use astria_eyre::eyre::{ self, WrapErr as _, diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/startup.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/startup.rs index 033b53f0a2..c80c170cab 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/startup.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/startup.rs @@ -4,7 +4,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::v1::sequencer_service_client::{ + generated::astria::sequencerblock::v1::sequencer_service_client::{ self, SequencerServiceClient, }, @@ -270,20 +270,21 @@ impl Startup { the sequencer logic." ); - let proto_tx = astria_core::generated::protocol::transaction::v1::Transaction::decode( - &*last_transaction.tx, - ) - .wrap_err_with(|| { - format!( + let proto_tx = + astria_core::generated::astria::protocol::transaction::v1::Transaction::decode( + &*last_transaction.tx, + ) + .wrap_err_with(|| { + format!( "failed to decode data in Sequencer CometBFT transaction as `{}`", - astria_core::generated::protocol::transaction::v1::Transaction::full_name(), + astria_core::generated::astria::protocol::transaction::v1::Transaction::full_name(), ) - })?; + })?; let tx = Transaction::try_from_raw(proto_tx).wrap_err_with(|| { format!( "failed to verify {}", - astria_core::generated::protocol::transaction::v1::Transaction::full_name() + astria_core::generated::astria::protocol::transaction::v1::Transaction::full_name() ) })?; diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/builder.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/builder.rs index 7b5593a0aa..62308d1572 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/builder.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/builder.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use astria_core::generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; +use astria_core::generated::astria::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; use astria_eyre::eyre::{ self, Context as _, diff --git a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs index 0bd72886a5..a7c195c862 100644 --- a/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs +++ b/crates/astria-bridge-withdrawer/src/bridge_withdrawer/submitter/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_client::{ self, SequencerServiceClient, diff --git a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_cometbft.rs b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_cometbft.rs index c3f23d53fc..f0e4d0cef4 100644 --- a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_cometbft.rs +++ b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_cometbft.rs @@ -332,7 +332,7 @@ fn prepare_broadcast_tx_sync_response(response: tx_sync::Response) -> Mock { /// Convert a wiremock request to an astria transaction pub fn tx_from_request(request: &wiremock::Request) -> Transaction { - use astria_core::generated::protocol::transaction::v1::Transaction as RawTransaction; + use astria_core::generated::astria::protocol::transaction::v1::Transaction as RawTransaction; use prost::Message as _; let wrapped_tx_sync_req: tendermint_rpc::request::Wrapper = diff --git a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_sequencer.rs b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_sequencer.rs index 66dc282e31..9a2971b790 100644 --- a/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_sequencer.rs +++ b/crates/astria-bridge-withdrawer/tests/blackbox/helpers/mock_sequencer.rs @@ -5,7 +5,7 @@ use std::{ use astria_core::{ self, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_server::{ SequencerService, SequencerServiceServer, diff --git a/crates/astria-cli/src/sequencer/threshold/sign.rs b/crates/astria-cli/src/sequencer/threshold/sign.rs index 93e65987fc..b5a3830f24 100644 --- a/crates/astria-cli/src/sequencer/threshold/sign.rs +++ b/crates/astria-cli/src/sequencer/threshold/sign.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use astria_core::generated::protocol::transaction::v1::{ +use astria_core::generated::astria::protocol::transaction::v1::{ Transaction, TransactionBody, }; diff --git a/crates/astria-cli/src/sequencer/threshold/verify.rs b/crates/astria-cli/src/sequencer/threshold/verify.rs index f616cb4475..ec4d027b46 100644 --- a/crates/astria-cli/src/sequencer/threshold/verify.rs +++ b/crates/astria-cli/src/sequencer/threshold/verify.rs @@ -1,4 +1,4 @@ -use astria_core::generated::protocol::transaction::v1::TransactionBody; +use astria_core::generated::astria::protocol::transaction::v1::TransactionBody; use color_eyre::eyre::{ self, WrapErr as _, diff --git a/crates/astria-composer/src/collectors/grpc.rs b/crates/astria-composer/src/collectors/grpc.rs index 18d95adb93..deeb580546 100644 --- a/crates/astria-composer/src/collectors/grpc.rs +++ b/crates/astria-composer/src/collectors/grpc.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use astria_core::{ - generated::composer::v1::{ + generated::astria::composer::v1::{ grpc_collector_service_server::GrpcCollectorService, SubmitRollupTransactionRequest, SubmitRollupTransactionResponse, diff --git a/crates/astria-composer/src/executor/builder.rs b/crates/astria-composer/src/executor/builder.rs index c388a63121..31a8ee04fa 100644 --- a/crates/astria-composer/src/executor/builder.rs +++ b/crates/astria-composer/src/executor/builder.rs @@ -6,7 +6,7 @@ use std::{ use astria_core::{ crypto::SigningKey, - generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, + generated::astria::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, primitive::v1::Address, protocol::transaction::v1::action::RollupDataSubmission, }; diff --git a/crates/astria-composer/src/executor/mod.rs b/crates/astria-composer/src/executor/mod.rs index a07b2e4fe6..62ed88e966 100644 --- a/crates/astria-composer/src/executor/mod.rs +++ b/crates/astria-composer/src/executor/mod.rs @@ -12,7 +12,7 @@ use std::{ use astria_core::{ crypto::SigningKey, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_client::{ self, SequencerServiceClient, diff --git a/crates/astria-composer/src/grpc.rs b/crates/astria-composer/src/grpc.rs index f9453b36ef..7c1d3b8ef1 100644 --- a/crates/astria-composer/src/grpc.rs +++ b/crates/astria-composer/src/grpc.rs @@ -9,7 +9,7 @@ use std::net::SocketAddr; use astria_core::{ - generated::composer::v1::grpc_collector_service_server::GrpcCollectorServiceServer, + generated::astria::composer::v1::grpc_collector_service_server::GrpcCollectorServiceServer, primitive::v1::asset, }; use astria_eyre::{ diff --git a/crates/astria-composer/tests/blackbox/grpc_collector.rs b/crates/astria-composer/tests/blackbox/grpc_collector.rs index f8286a25d8..18767ab0ab 100644 --- a/crates/astria-composer/tests/blackbox/grpc_collector.rs +++ b/crates/astria-composer/tests/blackbox/grpc_collector.rs @@ -1,7 +1,7 @@ use std::time::Duration; use astria_core::{ - generated::composer::v1::{ + generated::astria::composer::v1::{ grpc_collector_service_client::GrpcCollectorServiceClient, SubmitRollupTransactionRequest, }, diff --git a/crates/astria-composer/tests/blackbox/helper/mock_grpc_sequencer.rs b/crates/astria-composer/tests/blackbox/helper/mock_grpc_sequencer.rs index 23be34c31a..0897610967 100644 --- a/crates/astria-composer/tests/blackbox/helper/mock_grpc_sequencer.rs +++ b/crates/astria-composer/tests/blackbox/helper/mock_grpc_sequencer.rs @@ -5,7 +5,7 @@ use std::{ use astria_core::{ self, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_server::{ SequencerService, SequencerServiceServer, diff --git a/crates/astria-composer/tests/blackbox/helper/mod.rs b/crates/astria-composer/tests/blackbox/helper/mod.rs index 93f7f6a1a7..fca7da0f41 100644 --- a/crates/astria-composer/tests/blackbox/helper/mod.rs +++ b/crates/astria-composer/tests/blackbox/helper/mod.rs @@ -207,7 +207,7 @@ pub async fn loop_until_composer_is_ready(addr: SocketAddr) { } fn signed_tx_from_request(request: &Request) -> Transaction { - use astria_core::generated::protocol::transaction::v1::Transaction as RawTransaction; + use astria_core::generated::astria::protocol::transaction::v1::Transaction as RawTransaction; use prost::Message as _; let wrapped_tx_sync_req: request::Wrapper = diff --git a/crates/astria-conductor/src/celestia/block_verifier.rs b/crates/astria-conductor/src/celestia/block_verifier.rs index ff72aa8a7c..7b580b954e 100644 --- a/crates/astria-conductor/src/celestia/block_verifier.rs +++ b/crates/astria-conductor/src/celestia/block_verifier.rs @@ -221,7 +221,7 @@ mod tests { use std::collections::BTreeMap; use astria_core::{ - generated::sequencerblock::v1::SequencerBlockHeader as RawSequencerBlockHeader, + generated::astria::sequencerblock::v1::SequencerBlockHeader as RawSequencerBlockHeader, primitive::v1::RollupId, sequencerblock::v1::{ block::SequencerBlockHeader, diff --git a/crates/astria-conductor/src/celestia/convert.rs b/crates/astria-conductor/src/celestia/convert.rs index c2772d4f15..02861136d1 100644 --- a/crates/astria-conductor/src/celestia/convert.rs +++ b/crates/astria-conductor/src/celestia/convert.rs @@ -1,6 +1,6 @@ use astria_core::{ brotli::decompress_bytes, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ SubmittedMetadataList, SubmittedRollupDataList, }, diff --git a/crates/astria-conductor/src/executor/client.rs b/crates/astria-conductor/src/executor/client.rs index bbd0b550cf..1bab377d97 100644 --- a/crates/astria-conductor/src/executor/client.rs +++ b/crates/astria-conductor/src/executor/client.rs @@ -6,7 +6,7 @@ use astria_core::{ CommitmentState, GenesisInfo, }, - generated::{ + generated::astria::{ execution::{ v1 as raw, v1::execution_service_client::ExecutionServiceClient, diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index bfe794e6c1..2ae4e2c4b2 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -344,7 +344,7 @@ pub(super) fn map_sequencer_height_to_rollup_height( #[cfg(test)] mod tests { use astria_core::{ - generated::execution::v1 as raw, + generated::astria::execution::v1 as raw, Protobuf as _, }; use pbjson_types::Timestamp; diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index 164d8b0a20..e8ead9dc71 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -5,7 +5,7 @@ use astria_core::{ CommitmentState, GenesisInfo, }, - generated::execution::v1 as raw, + generated::astria::execution::v1 as raw, Protobuf as _, }; use bytes::Bytes; diff --git a/crates/astria-conductor/src/sequencer/client.rs b/crates/astria-conductor/src/sequencer/client.rs index 760a5ede27..e0e61d2229 100644 --- a/crates/astria-conductor/src/sequencer/client.rs +++ b/crates/astria-conductor/src/sequencer/client.rs @@ -3,7 +3,7 @@ use std::time::Duration; use astria_core::{ - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_client::SequencerServiceClient, GetFilteredSequencerBlockRequest, }, diff --git a/crates/astria-conductor/tests/blackbox/firm_only.rs b/crates/astria-conductor/tests/blackbox/firm_only.rs index 2fb2c43148..e634292a1c 100644 --- a/crates/astria-conductor/tests/blackbox/firm_only.rs +++ b/crates/astria-conductor/tests/blackbox/firm_only.rs @@ -5,7 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::execution::v1::{ +use astria_core::generated::astria::execution::v1::{ GetCommitmentStateRequest, GetGenesisInfoRequest, }; diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index 4119fe1bf6..981f4c31dc 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! block { (number: $number:expr,hash: $hash:expr,parent: $parent:expr $(,)?) => { - ::astria_core::generated::execution::v1::Block { + ::astria_core::generated::astria::execution::v1::Block { number: $number, hash: ::bytes::Bytes::from(Vec::from($hash)), parent_block_hash: ::bytes::Bytes::from(Vec::from($parent)), @@ -59,7 +59,7 @@ macro_rules! commitment_state { soft: (number: $soft_number:expr,hash: $soft_hash:expr,parent: $soft_parent:expr $(,)?), base_celestia_height: $base_celestia_height:expr $(,)? ) => { - ::astria_core::generated::execution::v1::CommitmentState { + ::astria_core::generated::astria::execution::v1::CommitmentState { firm: Some($crate::block!( number: $firm_number, hash: $firm_hash, @@ -98,7 +98,7 @@ macro_rules! genesis_info { $sequencer_height:expr,celestia_block_variance: $variance:expr $(,)? ) => { - ::astria_core::generated::execution::v1::GenesisInfo { + ::astria_core::generated::astria::execution::v1::GenesisInfo { rollup_id: Some($crate::ROLLUP_ID.to_raw()), sequencer_genesis_block_height: $sequencer_height, celestia_block_variance: $variance, @@ -312,7 +312,7 @@ macro_rules! mount_get_filtered_sequencer_block { ($test_env:ident, sequencer_height: $height:expr, delay: $delay:expr $(,)?) => { $test_env .mount_get_filtered_sequencer_block( - ::astria_core::generated::sequencerblock::v1::GetFilteredSequencerBlockRequest { + ::astria_core::generated::astria::sequencerblock::v1::GetFilteredSequencerBlockRequest { height: $height, rollup_ids: vec![$crate::ROLLUP_ID.to_raw()], }, @@ -385,12 +385,12 @@ macro_rules! mount_get_block { hash: $hash, parent: $parent, ); - let identifier = ::astria_core::generated::execution::v1::BlockIdentifier { + let identifier = ::astria_core::generated::astria::execution::v1::BlockIdentifier { identifier: Some( - ::astria_core::generated::execution::v1::block_identifier::Identifier::BlockNumber(block.number) + ::astria_core::generated::astria::execution::v1::block_identifier::Identifier::BlockNumber(block.number) )}; $test_env.mount_get_block( - ::astria_core::generated::execution::v1::GetBlockRequest { + ::astria_core::generated::astria::execution::v1::GetBlockRequest { identifier: Some(identifier), }, block, diff --git a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs index c0b8b7ff2f..457ef04e3d 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs @@ -3,7 +3,7 @@ use std::{ sync::Arc, }; -use astria_core::generated::{ +use astria_core::generated::astria::{ execution::v1::{ execution_service_server::{ ExecutionService, diff --git a/crates/astria-conductor/tests/blackbox/helpers/mod.rs b/crates/astria-conductor/tests/blackbox/helpers/mod.rs index da7dad5061..8890bc3f20 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mod.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mod.rs @@ -12,7 +12,7 @@ use astria_conductor::{ }; use astria_core::{ brotli::compress_bytes, - generated::{ + generated::astria::{ execution::v1::{ Block, CommitmentState, @@ -179,7 +179,7 @@ impl TestConductor { pub async fn mount_get_block( &self, expected_pbjson: S, - block: astria_core::generated::execution::v1::Block, + block: astria_core::generated::astria::execution::v1::Block, ) { use astria_grpc_mock::{ matcher::message_partial_pbjson, @@ -306,7 +306,7 @@ impl TestConductor { } pub async fn mount_get_genesis_info(&self, genesis_info: GenesisInfo) { - use astria_core::generated::execution::v1::GetGenesisInfoRequest; + use astria_core::generated::astria::execution::v1::GetGenesisInfoRequest; astria_grpc_mock::Mock::for_rpc_given( "get_genesis_info", astria_grpc_mock::matcher::message_type::(), @@ -318,7 +318,7 @@ impl TestConductor { } pub async fn mount_get_commitment_state(&self, commitment_state: CommitmentState) { - use astria_core::generated::execution::v1::GetCommitmentStateRequest; + use astria_core::generated::astria::execution::v1::GetCommitmentStateRequest; astria_grpc_mock::Mock::for_rpc_given( "get_commitment_state", @@ -381,7 +381,7 @@ impl TestConductor { commitment_state: CommitmentState, expected_calls: u64, ) -> astria_grpc_mock::MockGuard { - use astria_core::generated::execution::v1::UpdateCommitmentStateRequest; + use astria_core::generated::astria::execution::v1::UpdateCommitmentStateRequest; use astria_grpc_mock::{ matcher::message_partial_pbjson, response::constant_response, @@ -567,7 +567,7 @@ pub struct Blobs { #[must_use] pub fn make_blobs(heights: &[u32]) -> Blobs { - use astria_core::generated::sequencerblock::v1::{ + use astria_core::generated::astria::sequencerblock::v1::{ SubmittedMetadataList, SubmittedRollupDataList, }; diff --git a/crates/astria-conductor/tests/blackbox/soft_only.rs b/crates/astria-conductor/tests/blackbox/soft_only.rs index e82793c638..8fff4e8c1c 100644 --- a/crates/astria-conductor/tests/blackbox/soft_only.rs +++ b/crates/astria-conductor/tests/blackbox/soft_only.rs @@ -5,7 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::execution::v1::{ +use astria_core::generated::astria::execution::v1::{ GetCommitmentStateRequest, GetGenesisInfoRequest, }; diff --git a/crates/astria-core/CHANGELOG.md b/crates/astria-core/CHANGELOG.md index dd487eeab3..7a812add6d 100644 --- a/crates/astria-core/CHANGELOG.md +++ b/crates/astria-core/CHANGELOG.md @@ -15,6 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added method `TracePrefixed::leading_channel` to read the left-most channel of a trace prefixed ICS20 asset [#1768](https://github.com/astriaorg/astria/pull/1768) +### Changed + +- Move all Astria APIs generated from the Protobuf spec from `astria_core::generated` + to `astria_core::generated::astria` + [#1825](https://github.com/astriaorg/astria/pull/1825) + ### Removed - Removed method `TracePrefixed::last_channel` [#1768](https://github.com/astriaorg/astria/pull/1768) diff --git a/crates/astria-core/src/execution/v1/mod.rs b/crates/astria-core/src/execution/v1/mod.rs index 6daca96a42..3327cd0edc 100644 --- a/crates/astria-core/src/execution/v1/mod.rs +++ b/crates/astria-core/src/execution/v1/mod.rs @@ -2,7 +2,7 @@ use bytes::Bytes; use pbjson_types::Timestamp; use crate::{ - generated::execution::v1 as raw, + generated::astria::execution::v1 as raw, primitive::v1::{ IncorrectRollupIdLength, RollupId, @@ -43,7 +43,7 @@ enum GenesisInfoErrorKind { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::execution::v1::GenesisInfo") + serde(into = "crate::generated::astria::execution::v1::GenesisInfo") )] pub struct GenesisInfo { /// The rollup id which is used to identify the rollup txs. @@ -148,7 +148,7 @@ enum BlockErrorKind { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::execution::v1::Block") + serde(into = "crate::generated::astria::execution::v1::Block") )] pub struct Block { /// The block number @@ -380,7 +380,7 @@ impl CommitmentStateBuilder { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::execution::v1::CommitmentState") + serde(into = "crate::generated::astria::execution::v1::CommitmentState") )] pub struct CommitmentState { /// Soft commitment is the rollup block matching latest sequencer block. diff --git a/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs b/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs index 6bd2ba2fdc..0b38628394 100644 --- a/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs +++ b/crates/astria-core/src/generated/astria.protocol.transaction.v1.rs @@ -36,7 +36,7 @@ pub mod action { SudoAddressChange(super::SudoAddressChange), #[prost(message, tag = "51")] ValidatorUpdate( - crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate, + super::super::super::super::super::astria_vendored::tendermint::abci::ValidatorUpdate, ), #[prost(message, tag = "52")] IbcRelayerChange(super::IbcRelayerChange), diff --git a/crates/astria-core/src/generated/astria.protocol.transaction.v1alpha1.rs b/crates/astria-core/src/generated/astria.protocol.transaction.v1alpha1.rs index 75c1bf44b4..a310970f2b 100644 --- a/crates/astria-core/src/generated/astria.protocol.transaction.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.protocol.transaction.v1alpha1.rs @@ -36,7 +36,7 @@ pub mod action { SudoAddressChange(super::SudoAddressChange), #[prost(message, tag = "51")] ValidatorUpdate( - crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate, + super::super::super::super::super::astria_vendored::tendermint::abci::ValidatorUpdate, ), #[prost(message, tag = "52")] IbcRelayerChange(super::IbcRelayerChange), diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs deleted file mode 100644 index 612e5c9fcf..0000000000 --- a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.rs +++ /dev/null @@ -1,516 +0,0 @@ -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBlockCommitmentStreamRequest {} -impl ::prost::Name for GetBlockCommitmentStreamRequest { - const NAME: &'static str = "GetBlockCommitmentStreamRequest"; - const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!( - "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME - ) - } -} -/// Identifying metadata for blocks that have been successfully committed in the Sequencer. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SequencerBlockCommit { - /// Height of the sequencer block that was committed. - #[prost(uint64, tag = "1")] - pub height: u64, - /// Hash of the sequencer block that was committed. - #[prost(bytes = "bytes", tag = "2")] - pub block_hash: ::prost::bytes::Bytes, -} -impl ::prost::Name for SequencerBlockCommit { - const NAME: &'static str = "SequencerBlockCommit"; - const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!( - "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME - ) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBlockCommitmentStreamResponse { - #[prost(message, optional, tag = "1")] - pub commitment: ::core::option::Option, -} -impl ::prost::Name for GetBlockCommitmentStreamResponse { - const NAME: &'static str = "GetBlockCommitmentStreamResponse"; - const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!( - "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME - ) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetOptimisticBlockStreamRequest { - /// The rollup id for which the Sequencer block is being streamed. - #[prost(message, optional, tag = "1")] - pub rollup_id: ::core::option::Option, -} -impl ::prost::Name for GetOptimisticBlockStreamRequest { - const NAME: &'static str = "GetOptimisticBlockStreamRequest"; - const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!( - "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME - ) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetOptimisticBlockStreamResponse { - /// The optimistic Sequencer block that is being streamed, filtered for the provided rollup id. - #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option, -} -impl ::prost::Name for GetOptimisticBlockStreamResponse { - const NAME: &'static str = "GetOptimisticBlockStreamResponse"; - const PACKAGE: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!( - "astria.sequencerblock.optimisticblock.v1alpha1.{}", Self::NAME - ) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod optimistic_block_service_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// The Sequencer will serve this to the aucitoneer - #[derive(Debug, Clone)] - pub struct OptimisticBlockServiceClient { - inner: tonic::client::Grpc, - } - impl OptimisticBlockServiceClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl OptimisticBlockServiceClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> OptimisticBlockServiceClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - OptimisticBlockServiceClient::new( - InterceptedService::new(inner, interceptor), - ) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided - /// rollup id) to the Auctioneer. - pub async fn get_optimistic_block_stream( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response< - tonic::codec::Streaming, - >, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService", - "GetOptimisticBlockStream", - ), - ); - self.inner.server_streaming(req, path, codec).await - } - /// The Sequencer will stream the block commits to the Auctioneer. - pub async fn get_block_commitment_stream( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response< - tonic::codec::Streaming, - >, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService", - "GetBlockCommitmentStream", - ), - ); - self.inner.server_streaming(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod optimistic_block_service_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with OptimisticBlockServiceServer. - #[async_trait] - pub trait OptimisticBlockService: Send + Sync + 'static { - /// Server streaming response type for the GetOptimisticBlockStream method. - type GetOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetOptimisticBlockStreamResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// The Sequencer will stream the optimistic Sequencer block (filtered for the provided - /// rollup id) to the Auctioneer. - async fn get_optimistic_block_stream( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// Server streaming response type for the GetBlockCommitmentStream method. - type GetBlockCommitmentStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::GetBlockCommitmentStreamResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// The Sequencer will stream the block commits to the Auctioneer. - async fn get_block_commitment_stream( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - /// The Sequencer will serve this to the aucitoneer - #[derive(Debug)] - pub struct OptimisticBlockServiceServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl OptimisticBlockServiceServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> - for OptimisticBlockServiceServer - where - T: OptimisticBlockService, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetOptimisticBlockStream" => { - #[allow(non_camel_case_types)] - struct GetOptimisticBlockStreamSvc( - pub Arc, - ); - impl< - T: OptimisticBlockService, - > tonic::server::ServerStreamingService< - super::GetOptimisticBlockStreamRequest, - > for GetOptimisticBlockStreamSvc { - type Response = super::GetOptimisticBlockStreamResponse; - type ResponseStream = T::GetOptimisticBlockStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::GetOptimisticBlockStreamRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_optimistic_block_stream( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetOptimisticBlockStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService/GetBlockCommitmentStream" => { - #[allow(non_camel_case_types)] - struct GetBlockCommitmentStreamSvc( - pub Arc, - ); - impl< - T: OptimisticBlockService, - > tonic::server::ServerStreamingService< - super::GetBlockCommitmentStreamRequest, - > for GetBlockCommitmentStreamSvc { - type Response = super::GetBlockCommitmentStreamResponse; - type ResponseStream = T::GetBlockCommitmentStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::GetBlockCommitmentStreamRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_block_commitment_stream( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetBlockCommitmentStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for OptimisticBlockServiceServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService - for OptimisticBlockServiceServer { - const NAME: &'static str = "astria.sequencerblock.optimisticblock.v1alpha1.OptimisticBlockService"; - } -} diff --git a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs b/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs deleted file mode 100644 index bc159c576f..0000000000 --- a/crates/astria-core/src/generated/astria.sequencerblock.optimisticblock.v1alpha1.serde.rs +++ /dev/null @@ -1,460 +0,0 @@ -impl serde::Serialize for GetBlockCommitmentStreamRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetBlockCommitmentStreamRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(GetBlockCommitmentStreamRequest { - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetBlockCommitmentStreamResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.commitment.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse", len)?; - if let Some(v) = self.commitment.as_ref() { - struct_ser.serialize_field("commitment", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetBlockCommitmentStreamResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "commitment", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Commitment, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "commitment" => Ok(GeneratedField::Commitment), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetBlockCommitmentStreamResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut commitment__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Commitment => { - if commitment__.is_some() { - return Err(serde::de::Error::duplicate_field("commitment")); - } - commitment__ = map_.next_value()?; - } - } - } - Ok(GetBlockCommitmentStreamResponse { - commitment: commitment__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetBlockCommitmentStreamResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetOptimisticBlockStreamRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.rollup_id.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest", len)?; - if let Some(v) = self.rollup_id.as_ref() { - struct_ser.serialize_field("rollupId", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "rollup_id", - "rollupId", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - RollupId, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetOptimisticBlockStreamRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut rollup_id__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::RollupId => { - if rollup_id__.is_some() { - return Err(serde::de::Error::duplicate_field("rollupId")); - } - rollup_id__ = map_.next_value()?; - } - } - } - Ok(GetOptimisticBlockStreamRequest { - rollup_id: rollup_id__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetOptimisticBlockStreamResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.block.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse", len)?; - if let Some(v) = self.block.as_ref() { - struct_ser.serialize_field("block", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetOptimisticBlockStreamResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "block", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Block, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "block" => Ok(GeneratedField::Block), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetOptimisticBlockStreamResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut block__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Block => { - if block__.is_some() { - return Err(serde::de::Error::duplicate_field("block")); - } - block__ = map_.next_value()?; - } - } - } - Ok(GetOptimisticBlockStreamResponse { - block: block__, - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.GetOptimisticBlockStreamResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for SequencerBlockCommit { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.height != 0 { - len += 1; - } - if !self.block_hash.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit", len)?; - if self.height != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?; - } - if !self.block_hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&self.block_hash).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for SequencerBlockCommit { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "height", - "block_hash", - "blockHash", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Height, - BlockHash, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "height" => Ok(GeneratedField::Height), - "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = SequencerBlockCommit; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut height__ = None; - let mut block_hash__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Height => { - if height__.is_some() { - return Err(serde::de::Error::duplicate_field("height")); - } - height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::BlockHash => { - if block_hash__.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")); - } - block_hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(SequencerBlockCommit { - height: height__.unwrap_or_default(), - block_hash: block_hash__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.sequencerblock.optimisticblock.v1alpha1.SequencerBlockCommit", FIELDS, GeneratedVisitor) - } -} diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index 963dd5bbe3..41e186e2d7 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -38,151 +38,155 @@ pub mod astria_vendored { } #[path = ""] -pub mod bundle { - pub mod v1alpha1 { - include!("astria.bundle.v1alpha1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.bundle.v1alpha1.serde.rs"); - } - } -} - -#[path = ""] -pub mod execution { - pub mod v1 { - include!("astria.execution.v1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.execution.v1.serde.rs"); - } - } -} - -#[path = ""] -pub mod primitive { - pub mod v1 { - include!("astria.primitive.v1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.primitive.v1.serde.rs"); - } - } -} - -#[path = ""] -pub mod protocol { +pub mod astria { #[path = ""] - pub mod accounts { - #[path = "astria.protocol.accounts.v1.rs"] - pub mod v1; - } - #[path = ""] - pub mod asset { - #[path = "astria.protocol.asset.v1.rs"] - pub mod v1; - } - #[path = ""] - pub mod bridge { - #[path = "astria.protocol.bridge.v1.rs"] - pub mod v1; - } - #[path = ""] - pub mod fees { - #[path = "astria.protocol.fees.v1.rs"] - pub mod v1 { - include!("astria.protocol.fees.v1.rs"); + pub mod bundle { + pub mod v1alpha1 { + include!("astria.bundle.v1alpha1.rs"); #[cfg(feature = "serde")] - mod _serde_impls { + mod _serde_impl { use super::*; - include!("astria.protocol.fees.v1.serde.rs"); + include!("astria.bundle.v1alpha1.serde.rs"); } } } + #[path = ""] - pub mod genesis { + pub mod execution { pub mod v1 { - include!("astria.protocol.genesis.v1.rs"); + include!("astria.execution.v1.rs"); #[cfg(feature = "serde")] - mod _serde_impls { + mod _serde_impl { use super::*; - include!("astria.protocol.genesis.v1.serde.rs"); + include!("astria.execution.v1.serde.rs"); } } } + #[path = ""] - pub mod memos { + pub mod primitive { pub mod v1 { - include!("astria.protocol.memos.v1.rs"); + include!("astria.primitive.v1.rs"); #[cfg(feature = "serde")] - mod _serde_impls { + mod _serde_impl { use super::*; - include!("astria.protocol.memos.v1.serde.rs"); + include!("astria.primitive.v1.serde.rs"); } } } + #[path = ""] - pub mod transaction { - pub mod v1 { - include!("astria.protocol.transaction.v1.rs"); + pub mod protocol { + #[path = ""] + pub mod accounts { + #[path = "astria.protocol.accounts.v1.rs"] + pub mod v1; + } + #[path = ""] + pub mod asset { + #[path = "astria.protocol.asset.v1.rs"] + pub mod v1; + } + #[path = ""] + pub mod bridge { + #[path = "astria.protocol.bridge.v1.rs"] + pub mod v1; + } + #[path = ""] + pub mod fees { + #[path = "astria.protocol.fees.v1.rs"] + pub mod v1 { + include!("astria.protocol.fees.v1.rs"); - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.protocol.transaction.v1.serde.rs"); + #[cfg(feature = "serde")] + mod _serde_impls { + use super::*; + include!("astria.protocol.fees.v1.serde.rs"); + } } } - } -} + #[path = ""] + pub mod genesis { + pub mod v1 { + include!("astria.protocol.genesis.v1.rs"); -#[path = ""] -pub mod sequencerblock { - pub mod v1alpha1 { - include!("astria.sequencerblock.v1alpha1.rs"); + #[cfg(feature = "serde")] + mod _serde_impls { + use super::*; + include!("astria.protocol.genesis.v1.serde.rs"); + } + } + } + #[path = ""] + pub mod memos { + pub mod v1 { + include!("astria.protocol.memos.v1.rs"); - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.sequencerblock.v1alpha1.serde.rs"); + #[cfg(feature = "serde")] + mod _serde_impls { + use super::*; + include!("astria.protocol.memos.v1.serde.rs"); + } + } } - } - pub mod v1 { - include!("astria.sequencerblock.v1.rs"); + #[path = ""] + pub mod transaction { + pub mod v1 { + include!("astria.protocol.transaction.v1.rs"); - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.sequencerblock.v1.serde.rs"); + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.protocol.transaction.v1.serde.rs"); + } + } } } - pub mod optimisticblock { + #[path = ""] + pub mod sequencerblock { pub mod v1alpha1 { - include!("astria.sequencerblock.optimisticblock.v1alpha1.rs"); + include!("astria.sequencerblock.v1alpha1.rs"); #[cfg(feature = "serde")] mod _serde_impl { use super::*; - include!("astria.sequencerblock.optimisticblock.v1alpha1.serde.rs"); + include!("astria.sequencerblock.v1alpha1.serde.rs"); + } + } + + pub mod v1 { + include!("astria.sequencerblock.v1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.sequencerblock.v1.serde.rs"); + } + } + + pub mod optimistic { + pub mod v1alpha1 { + include!("astria.sequencerblock.optimistic.v1alpha1.rs"); + + #[cfg(feature = "serde")] + mod _serde_impl { + use super::*; + include!("astria.sequencerblock.optimistic.v1alpha1.serde.rs"); + } } } } -} -#[path = ""] -pub mod composer { - #[path = "astria.composer.v1.rs"] - pub mod v1; + #[path = ""] + pub mod composer { + #[path = "astria.composer.v1.rs"] + pub mod v1; + } } #[path = ""] diff --git a/crates/astria-core/src/primitive/v1/mod.rs b/crates/astria-core/src/primitive/v1/mod.rs index 56cb6922b5..f433b623fe 100644 --- a/crates/astria-core/src/primitive/v1/mod.rs +++ b/crates/astria-core/src/primitive/v1/mod.rs @@ -17,7 +17,7 @@ use sha2::{ }; use crate::{ - generated::primitive::v1 as raw, + generated::astria::primitive::v1 as raw, Protobuf, }; diff --git a/crates/astria-core/src/primitive/v1/u128.rs b/crates/astria-core/src/primitive/v1/u128.rs index ee77646a27..581f5b5ecc 100644 --- a/crates/astria-core/src/primitive/v1/u128.rs +++ b/crates/astria-core/src/primitive/v1/u128.rs @@ -1,6 +1,6 @@ //! Transformations of compiled protobuf types to other types. -use crate::generated::primitive::v1::Uint128; +use crate::generated::astria::primitive::v1::Uint128; impl From for Uint128 { fn from(primitive: u128) -> Self { let [ @@ -48,7 +48,8 @@ impl<'a> From<&'a u128> for Uint128 { #[cfg(test)] mod tests { - use crate::generated::primitive::v1::Uint128; + use super::Uint128; + #[track_caller] fn u128_roundtrip_check(expected: u128) { let pb: Uint128 = expected.into(); diff --git a/crates/astria-core/src/protocol/account/mod.rs b/crates/astria-core/src/protocol/account/mod.rs index 423eaae929..a3a6d96c3f 100644 --- a/crates/astria-core/src/protocol/account/mod.rs +++ b/crates/astria-core/src/protocol/account/mod.rs @@ -1,3 +1 @@ pub mod v1; - -use crate::generated::protocol::accounts::v1 as raw; diff --git a/crates/astria-core/src/protocol/account/v1/mod.rs b/crates/astria-core/src/protocol/account/v1/mod.rs index 697696ad66..1b16953fdc 100644 --- a/crates/astria-core/src/protocol/account/v1/mod.rs +++ b/crates/astria-core/src/protocol/account/v1/mod.rs @@ -1,7 +1,9 @@ -use super::raw; -use crate::primitive::v1::asset::{ - Denom, - ParseDenomError, +use crate::{ + generated::astria::protocol::accounts::v1 as raw, + primitive::v1::asset::{ + Denom, + ParseDenomError, + }, }; #[derive(Debug, thiserror::Error)] diff --git a/crates/astria-core/src/protocol/asset/mod.rs b/crates/astria-core/src/protocol/asset/mod.rs index bd31b9c85b..a3a6d96c3f 100644 --- a/crates/astria-core/src/protocol/asset/mod.rs +++ b/crates/astria-core/src/protocol/asset/mod.rs @@ -1,3 +1 @@ pub mod v1; - -use crate::generated::protocol::asset::v1 as raw; diff --git a/crates/astria-core/src/protocol/asset/v1/mod.rs b/crates/astria-core/src/protocol/asset/v1/mod.rs index c9330e32bf..9d8ec55c28 100644 --- a/crates/astria-core/src/protocol/asset/v1/mod.rs +++ b/crates/astria-core/src/protocol/asset/v1/mod.rs @@ -1,8 +1,10 @@ -use super::raw; -use crate::primitive::v1::asset::{ - self, - Denom, - ParseDenomError, +use crate::{ + generated::astria::protocol::asset::v1 as raw, + primitive::v1::asset::{ + self, + Denom, + ParseDenomError, + }, }; #[derive(Debug, thiserror::Error)] diff --git a/crates/astria-core/src/protocol/bridge/mod.rs b/crates/astria-core/src/protocol/bridge/mod.rs index f8b278c8f1..cab4a9fd57 100644 --- a/crates/astria-core/src/protocol/bridge/mod.rs +++ b/crates/astria-core/src/protocol/bridge/mod.rs @@ -1,3 +1,3 @@ pub mod v1; -use crate::generated::protocol::bridge::v1 as raw; +use crate::generated::astria::protocol::bridge::v1 as raw; diff --git a/crates/astria-core/src/protocol/fees/v1.rs b/crates/astria-core/src/protocol/fees/v1.rs index 400a63651d..f77441a90c 100644 --- a/crates/astria-core/src/protocol/fees/v1.rs +++ b/crates/astria-core/src/protocol/fees/v1.rs @@ -1,7 +1,7 @@ use prost::Name as _; use crate::{ - generated::protocol::fees::v1 as raw, + generated::astria::protocol::fees::v1 as raw, primitive::v1::asset, Protobuf, }; diff --git a/crates/astria-core/src/protocol/genesis/v1.rs b/crates/astria-core/src/protocol/genesis/v1.rs index 69bb2269bd..b03fb3cf8b 100644 --- a/crates/astria-core/src/protocol/genesis/v1.rs +++ b/crates/astria-core/src/protocol/genesis/v1.rs @@ -3,7 +3,7 @@ use std::convert::Infallible; pub use penumbra_ibc::params::IBCParameters; use crate::{ - generated::protocol::genesis::v1 as raw, + generated::astria::protocol::genesis::v1 as raw, primitive::v1::{ asset::{ self, diff --git a/crates/astria-core/src/protocol/memos/v1.rs b/crates/astria-core/src/protocol/memos/v1.rs index 796eea7f7b..9e9aa76cf2 100644 --- a/crates/astria-core/src/protocol/memos/v1.rs +++ b/crates/astria-core/src/protocol/memos/v1.rs @@ -1,4 +1,4 @@ -pub use crate::generated::protocol::memos::v1::{ +pub use crate::generated::astria::protocol::memos::v1::{ Ics20TransferDeposit, Ics20WithdrawalFromRollup, }; diff --git a/crates/astria-core/src/protocol/transaction/v1/mod.rs b/crates/astria-core/src/protocol/transaction/v1/mod.rs index 6bfbd5b22c..59616514d8 100644 --- a/crates/astria-core/src/protocol/transaction/v1/mod.rs +++ b/crates/astria-core/src/protocol/transaction/v1/mod.rs @@ -11,7 +11,7 @@ use crate::{ SigningKey, VerificationKey, }, - generated::protocol::transaction::v1 as raw, + generated::astria::protocol::transaction::v1 as raw, primitive::v1::{ TransactionId, ADDRESS_LEN, diff --git a/crates/astria-core/src/sequencerblock/v1/block.rs b/crates/astria-core/src/sequencerblock/v1/block.rs index 48f8907fac..44b6eb98c7 100644 --- a/crates/astria-core/src/sequencerblock/v1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1/block.rs @@ -784,8 +784,9 @@ impl SequencerBlock { let mut rollup_datas = IndexMap::new(); for elem in data_list { - let raw_tx = crate::generated::protocol::transaction::v1::Transaction::decode(&*elem) - .map_err(SequencerBlockError::transaction_protobuf_decode)?; + let raw_tx = + crate::generated::astria::protocol::transaction::v1::Transaction::decode(&*elem) + .map_err(SequencerBlockError::transaction_protobuf_decode)?; let tx = Transaction::try_from_raw(raw_tx) .map_err(SequencerBlockError::raw_signed_transaction_conversion)?; for action in tx.into_unsigned().into_actions() { @@ -1363,7 +1364,7 @@ impl FilteredSequencerBlockError { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::sequencerblock::v1::Deposit") + serde(into = "crate::generated::astria::sequencerblock::v1::Deposit") )] pub struct Deposit { // the address on the sequencer to which the funds were sent to. diff --git a/crates/astria-core/src/sequencerblock/v1/mod.rs b/crates/astria-core/src/sequencerblock/v1/mod.rs index 966696c9ff..9685f452f6 100644 --- a/crates/astria-core/src/sequencerblock/v1/mod.rs +++ b/crates/astria-core/src/sequencerblock/v1/mod.rs @@ -16,7 +16,7 @@ use sha2::{ }; use crate::{ - generated::sequencerblock::v1 as raw, + generated::astria::sequencerblock::v1 as raw, primitive::v1::{ derive_merkle_tree_from_rollup_txs, IncorrectRollupIdLength, diff --git a/crates/astria-sequencer-client/src/extension_trait.rs b/crates/astria-sequencer-client/src/extension_trait.rs index 59cab0f7e5..96eed49ef8 100644 --- a/crates/astria-sequencer-client/src/extension_trait.rs +++ b/crates/astria-sequencer-client/src/extension_trait.rs @@ -450,7 +450,7 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::accounts::v1::BalanceResponse::decode( + astria_core::generated::astria::protocol::accounts::v1::BalanceResponse::decode( &*response.value, ) .map_err(|e| { @@ -492,7 +492,7 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::asset::v1::AllowedFeeAssetsResponse::decode( + astria_core::generated::astria::protocol::asset::v1::AllowedFeeAssetsResponse::decode( &*response.value, ) .map_err(|e| { @@ -532,14 +532,12 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::accounts::v1::NonceResponse::decode(&*response.value) - .map_err(|e| { - Error::abci_query_deserialization( - "astria.sequencer.v1.NonceResponse", - response, - e, - ) - })?; + astria_core::generated::astria::protocol::accounts::v1::NonceResponse::decode( + &*response.value, + ) + .map_err(|e| { + Error::abci_query_deserialization("astria.sequencer.v1.NonceResponse", response, e) + })?; Ok(proto_response.to_native()) } @@ -567,7 +565,7 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::bridge::v1::BridgeAccountInfoResponse::decode( + astria_core::generated::astria::protocol::bridge::v1::BridgeAccountInfoResponse::decode( &*response.value, ) .map_err(|e| { @@ -599,7 +597,7 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::bridge::v1::BridgeAccountLastTxHashResponse::decode( + astria_core::generated::astria::protocol::bridge::v1::BridgeAccountLastTxHashResponse::decode( &*response.value, ) .map_err(|e| { @@ -631,7 +629,7 @@ pub trait SequencerClientExt: Client { .map_err(|e| Error::tendermint_rpc("abci_query", e))?; let proto_response = - astria_core::generated::protocol::fees::v1::TransactionFeeResponse::decode( + astria_core::generated::astria::protocol::fees::v1::TransactionFeeResponse::decode( &*response.value, ) .map_err(|e| { diff --git a/crates/astria-sequencer-client/src/tests/http.rs b/crates/astria-sequencer-client/src/tests/http.rs index 23f1ee481c..3923c6178a 100644 --- a/crates/astria-sequencer-client/src/tests/http.rs +++ b/crates/astria-sequencer-client/src/tests/http.rs @@ -2,7 +2,7 @@ use std::time::Duration; use astria_core::{ crypto::SigningKey, - generated::protocol::{ + generated::astria::protocol::{ asset::v1::AllowedFeeAssetsResponse, fees::v1::TransactionFee, }, @@ -170,7 +170,7 @@ fn create_signed_transaction() -> Transaction { #[tokio::test] async fn get_latest_nonce() { - use astria_core::generated::protocol::accounts::v1::NonceResponse; + use astria_core::generated::astria::protocol::accounts::v1::NonceResponse; let MockSequencer { server, client, @@ -197,7 +197,7 @@ async fn get_latest_nonce() { #[tokio::test] async fn get_latest_balance() { - use astria_core::generated::protocol::accounts::v1::{ + use astria_core::generated::astria::protocol::accounts::v1::{ AssetBalance, BalanceResponse, }; @@ -262,7 +262,7 @@ async fn get_allowed_fee_assets() { #[tokio::test] async fn get_bridge_account_info() { use astria_core::{ - generated::protocol::bridge::v1::BridgeAccountInfoResponse, + generated::astria::protocol::bridge::v1::BridgeAccountInfoResponse, primitive::v1::RollupId, }; @@ -294,7 +294,7 @@ async fn get_bridge_account_info() { #[tokio::test] async fn get_bridge_account_last_transaction_hash() { - use astria_core::generated::protocol::bridge::v1::BridgeAccountLastTxHashResponse; + use astria_core::generated::astria::protocol::bridge::v1::BridgeAccountLastTxHashResponse; let MockSequencer { server, @@ -324,7 +324,7 @@ async fn get_bridge_account_last_transaction_hash() { #[tokio::test] async fn get_transaction_fee() { - use astria_core::generated::protocol::fees::v1::TransactionFeeResponse; + use astria_core::generated::astria::protocol::fees::v1::TransactionFeeResponse; let MockSequencer { server, diff --git a/crates/astria-sequencer-relayer/src/relayer/builder.rs b/crates/astria-sequencer-relayer/src/relayer/builder.rs index 0609af19ce..fd4d3fbf17 100644 --- a/crates/astria-sequencer-relayer/src/relayer/builder.rs +++ b/crates/astria-sequencer-relayer/src/relayer/builder.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use astria_core::generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; +use astria_core::generated::astria::sequencerblock::v1::sequencer_service_client::SequencerServiceClient; use astria_eyre::eyre::{ self, WrapErr as _, diff --git a/crates/astria-sequencer-relayer/src/relayer/mod.rs b/crates/astria-sequencer-relayer/src/relayer/mod.rs index 54c613ee81..e4e58e0cb7 100644 --- a/crates/astria-sequencer-relayer/src/relayer/mod.rs +++ b/crates/astria-sequencer-relayer/src/relayer/mod.rs @@ -5,7 +5,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, + generated::astria::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, sequencerblock::v1::SequencerBlock, }; use astria_eyre::eyre::{ diff --git a/crates/astria-sequencer-relayer/src/relayer/read.rs b/crates/astria-sequencer-relayer/src/relayer/read.rs index 58147fdfd2..6525f3e528 100644 --- a/crates/astria-sequencer-relayer/src/relayer/read.rs +++ b/crates/astria-sequencer-relayer/src/relayer/read.rs @@ -8,7 +8,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_client::SequencerServiceClient, GetSequencerBlockRequest, }, diff --git a/crates/astria-sequencer-relayer/src/relayer/write/conversion.rs b/crates/astria-sequencer-relayer/src/relayer/write/conversion.rs index 38733634d2..82d425ff89 100644 --- a/crates/astria-sequencer-relayer/src/relayer/write/conversion.rs +++ b/crates/astria-sequencer-relayer/src/relayer/write/conversion.rs @@ -10,7 +10,7 @@ use std::{ use astria_core::{ brotli::compress_bytes, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ SubmittedMetadata, SubmittedMetadataList, SubmittedRollupData, diff --git a/crates/astria-sequencer-relayer/tests/blackbox/helpers/mock_sequencer_server.rs b/crates/astria-sequencer-relayer/tests/blackbox/helpers/mock_sequencer_server.rs index 82534b292a..33ff34d989 100644 --- a/crates/astria-sequencer-relayer/tests/blackbox/helpers/mock_sequencer_server.rs +++ b/crates/astria-sequencer-relayer/tests/blackbox/helpers/mock_sequencer_server.rs @@ -4,7 +4,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ sequencer_service_server::{ SequencerService, SequencerServiceServer, diff --git a/crates/astria-sequencer-utils/src/blob_parser.rs b/crates/astria-sequencer-utils/src/blob_parser.rs index c729751c96..6fb450ff90 100644 --- a/crates/astria-sequencer-utils/src/blob_parser.rs +++ b/crates/astria-sequencer-utils/src/blob_parser.rs @@ -13,7 +13,7 @@ use std::{ use astria_core::{ brotli::decompress_bytes, - generated::sequencerblock::v1::{ + generated::astria::sequencerblock::v1::{ rollup_data::Value as RawRollupDataValue, Deposit as RawDeposit, RollupData as RawRollupData, diff --git a/crates/astria-sequencer-utils/src/genesis_example.rs b/crates/astria-sequencer-utils/src/genesis_example.rs index a464fd26c2..6d7d277719 100644 --- a/crates/astria-sequencer-utils/src/genesis_example.rs +++ b/crates/astria-sequencer-utils/src/genesis_example.rs @@ -5,7 +5,7 @@ use std::{ }; use astria_core::{ - generated::protocol::genesis::v1::{ + generated::astria::protocol::genesis::v1::{ AddressPrefixes, GenesisFees, IbcParameters, @@ -91,8 +91,8 @@ fn address_prefixes() -> AddressPrefixes { } #[expect(clippy::too_many_lines, reason = "all lines reasonably necessary")] -fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::GenesisAppState { - astria_core::generated::protocol::genesis::v1::GenesisAppState { +fn proto_genesis_state() -> astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { + astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { accounts: accounts().into_iter().map(Protobuf::into_raw).collect(), address_prefixes: Some(address_prefixes()), authority_sudo_address: Some(alice().to_raw()), diff --git a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs index 6c46e5c6e9..e2852fd225 100644 --- a/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs +++ b/crates/astria-sequencer/src/app/benchmark_and_test_utils.rs @@ -60,7 +60,7 @@ pub(crate) const TED_ADDRESS: &str = "4c4f91d8a918357ab5f6f19c1e179968fc39bb44"; pub(crate) fn address_prefixes() -> AddressPrefixes { AddressPrefixes::try_from_raw( - astria_core::generated::protocol::genesis::v1::AddressPrefixes { + astria_core::generated::astria::protocol::genesis::v1::AddressPrefixes { base: crate::benchmark_and_test_utils::ASTRIA_PREFIX.into(), ibc_compat: crate::benchmark_and_test_utils::ASTRIA_COMPAT_PREFIX.into(), }, @@ -129,9 +129,9 @@ pub(crate) fn default_fees() -> astria_core::protocol::genesis::v1::GenesisFees } } -pub(crate) fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::GenesisAppState -{ - use astria_core::generated::protocol::genesis::v1::{ +pub(crate) fn proto_genesis_state() +-> astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { + use astria_core::generated::astria::protocol::genesis::v1::{ GenesisAppState, IbcParameters, }; diff --git a/crates/astria-sequencer/src/app/benchmarks.rs b/crates/astria-sequencer/src/app/benchmarks.rs index 0b6aa3ef2b..390f650de1 100644 --- a/crates/astria-sequencer/src/app/benchmarks.rs +++ b/crates/astria-sequencer/src/app/benchmarks.rs @@ -61,7 +61,7 @@ impl Fixture { .collect::>(); let first_address = accounts.first().cloned().unwrap().address; let genesis_state = GenesisAppState::try_from_raw( - astria_core::generated::protocol::genesis::v1::GenesisAppState { + astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { accounts, authority_sudo_address: first_address.clone(), ibc_sudo_address: first_address.clone(), diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index e116664a79..d2e0ca7b48 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -22,7 +22,7 @@ use std::{ }; use astria_core::{ - generated::protocol::transaction::v1 as raw, + generated::astria::protocol::transaction::v1 as raw, protocol::{ abci::AbciErrorCode, genesis::v1::GenesisAppState, diff --git a/crates/astria-sequencer/src/app/tests_app/mod.rs b/crates/astria-sequencer/src/app/tests_app/mod.rs index 1ca46cec4d..b91e107105 100644 --- a/crates/astria-sequencer/src/app/tests_app/mod.rs +++ b/crates/astria-sequencer/src/app/tests_app/mod.rs @@ -302,7 +302,7 @@ async fn app_transfer_block_fees_to_sudo() { #[tokio::test] async fn app_create_sequencer_block_with_sequenced_data_and_deposits() { use astria_core::{ - generated::sequencerblock::v1::RollupData as RawRollupData, + generated::astria::sequencerblock::v1::RollupData as RawRollupData, sequencerblock::v1::block::RollupData, }; diff --git a/crates/astria-sequencer/src/app/tests_breaking_changes.rs b/crates/astria-sequencer/src/app/tests_breaking_changes.rs index 031538a057..6a7d922e1c 100644 --- a/crates/astria-sequencer/src/app/tests_breaking_changes.rs +++ b/crates/astria-sequencer/src/app/tests_breaking_changes.rs @@ -188,7 +188,7 @@ async fn app_execute_transaction_with_every_action_snapshot() { }); acc.into_iter().map(Protobuf::into_raw).collect() }; - let genesis_state = astria_core::generated::protocol::genesis::v1::GenesisAppState { + let genesis_state = astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { accounts, authority_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()), ibc_sudo_address: Some(alice.try_address(ASTRIA_PREFIX).unwrap().to_raw()), diff --git a/crates/astria-sequencer/src/app/tests_execute_transaction.rs b/crates/astria-sequencer/src/app/tests_execute_transaction.rs index 76b1a1ae8e..ecc7111601 100644 --- a/crates/astria-sequencer/src/app/tests_execute_transaction.rs +++ b/crates/astria-sequencer/src/app/tests_execute_transaction.rs @@ -75,8 +75,8 @@ use crate::{ utils::create_deposit_event, }; -fn proto_genesis_state() -> astria_core::generated::protocol::genesis::v1::GenesisAppState { - astria_core::generated::protocol::genesis::v1::GenesisAppState { +fn proto_genesis_state() -> astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { + astria_core::generated::astria::protocol::genesis::v1::GenesisAppState { authority_sudo_address: Some( get_alice_signing_key() .try_address(ASTRIA_PREFIX) diff --git a/crates/astria-sequencer/src/bridge/query.rs b/crates/astria-sequencer/src/bridge/query.rs index 2e3ff25ba2..ed5d1c5177 100644 --- a/crates/astria-sequencer/src/bridge/query.rs +++ b/crates/astria-sequencer/src/bridge/query.rs @@ -293,7 +293,7 @@ fn preprocess_request(params: &[(String, String)]) -> Result) -> (Consensus, Mempool) { let accounts = if let Some(funded_key) = funded_key { - vec![astria_core::generated::protocol::genesis::v1::Account { - address: Some( - crate::benchmark_and_test_utils::astria_address(funded_key.address_bytes()) - .to_raw(), - ), - balance: Some(10u128.pow(19).into()), - }] + vec![ + astria_core::generated::astria::protocol::genesis::v1::Account { + address: Some( + crate::benchmark_and_test_utils::astria_address(funded_key.address_bytes()) + .to_raw(), + ), + balance: Some(10u128.pow(19).into()), + }, + ] } else { vec![] }; diff --git a/crates/astria-sequencer/src/service/info/mod.rs b/crates/astria-sequencer/src/service/info/mod.rs index 23b863aebc..a233359274 100644 --- a/crates/astria-sequencer/src/service/info/mod.rs +++ b/crates/astria-sequencer/src/service/info/mod.rs @@ -212,7 +212,7 @@ mod tests { #[tokio::test] async fn handle_balance_query() { use astria_core::{ - generated::protocol::accounts::v1 as raw, + generated::astria::protocol::accounts::v1 as raw, protocol::account::v1::AssetBalance, }; @@ -279,7 +279,7 @@ mod tests { #[tokio::test] async fn handle_denom_query() { - use astria_core::generated::protocol::asset::v1 as raw; + use astria_core::generated::astria::protocol::asset::v1 as raw; let storage = cnidarium::TempStorage::new().await.unwrap(); let mut state = StateDelta::new(storage.latest_snapshot()); @@ -323,7 +323,7 @@ mod tests { #[tokio::test] async fn handle_allowed_fee_assets_query() { - use astria_core::generated::protocol::asset::v1 as raw; + use astria_core::generated::astria::protocol::asset::v1 as raw; let storage = cnidarium::TempStorage::new().await.unwrap(); let mut state = StateDelta::new(storage.latest_snapshot()); diff --git a/crates/astria-sequencer/src/service/mempool/mod.rs b/crates/astria-sequencer/src/service/mempool/mod.rs index 88b59a5b77..67cfa9a48d 100644 --- a/crates/astria-sequencer/src/service/mempool/mod.rs +++ b/crates/astria-sequencer/src/service/mempool/mod.rs @@ -10,7 +10,7 @@ use std::{ }; use astria_core::{ - generated::protocol::transaction::v1 as raw, + generated::astria::protocol::transaction::v1 as raw, primitive::v1::asset::IbcPrefixed, protocol::{ abci::AbciErrorCode, diff --git a/tools/protobuf-compiler/CHANGELOG.md b/tools/protobuf-compiler/CHANGELOG.md new file mode 100644 index 0000000000..a017ade42d --- /dev/null +++ b/tools/protobuf-compiler/CHANGELOG.md @@ -0,0 +1,15 @@ + + +# 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.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Clears target directory before generating new code. + [#1825](https://github.com/astriaorg/astria/pull/1825) diff --git a/tools/protobuf-compiler/src/main.rs b/tools/protobuf-compiler/src/main.rs index 1034672e90..20cf898353 100644 --- a/tools/protobuf-compiler/src/main.rs +++ b/tools/protobuf-compiler/src/main.rs @@ -1,3 +1,8 @@ +//! Generates Rust code of protobuf specs located in proto/ and writes +//! the result to crates/astria-core/src/generated. +//! +//! This tool will delete everything in crates/astria-core/src/generated (except +//! mod.rs). use std::{ collections::{ HashMap, @@ -59,23 +64,16 @@ fn main() { let files = find_protos(src_dir); + purge_out_dir(&out_dir); + tonic_build::configure() .build_client(true) .build_server(true) .emit_rerun_if_changed(false) - .bytes([ - ".astria", - ".celestia", - ".cosmos", - ".tendermint", - ]) + .bytes([".astria", ".celestia", ".cosmos", ".tendermint"]) .client_mod_attribute(".", "#[cfg(feature=\"client\")]") .server_mod_attribute(".", "#[cfg(feature=\"server\")]") .extern_path(".astria_vendored.penumbra", "::penumbra-proto") - .extern_path( - ".astria_vendored.tendermint.abci.ValidatorUpdate", - "crate::generated::astria_vendored::tendermint::abci::ValidatorUpdate", - ) .type_attribute(".astria.primitive.v1.Uint128", "#[derive(Copy)]") .type_attribute( ".astria.protocol.genesis.v1.IbcParameters", @@ -206,7 +204,8 @@ fn get_buf_from_env() -> PathBuf { "linux" => "You can download it from https://github.com/bufbuild/buf/releases; if you are on Arch Linux, install it from the AUR with `rua install buf` or another helper", _other => "Check if there is a precompiled version for your OS at https://github.com/bufbuild/buf/releases" }; - let error_msg = "Could not find `buf` installation and this build crate cannot proceed without + let error_msg = "Could not find `buf` installation and this build crate cannot proceed \ + without this knowledge. If `buf` is installed and this crate had trouble finding it, you can set the `BUF` environment variable with the specific path to your installed `buf` binary."; @@ -217,3 +216,25 @@ fn get_buf_from_env() -> PathBuf { .or_else(|| which::which("buf").ok()) .expect(&msg) } + +fn purge_out_dir(path: impl AsRef) { + for entry in read_dir(path) + .expect("should be able to read target folder for generated files") + .flatten() + { + // skip mod.rs as it's assumed to be the only non-generated file in the out dir. + if entry + .path() + .file_name() + .expect("every entry in the generated file out dir should have a name") + == "mod.rs" + { + continue; + } + + std::fs::remove_file(entry.path()).expect( + "all entries in the out dir should be generated files, and the out dir is expected to \ + have read, write, execute permissions set", + ); + } +} From 837ae4ebdb541b5d7d69ef6b0e56de2c2fcea186 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 20 Nov 2024 15:46:48 +0100 Subject: [PATCH 07/73] set msrv to 1.81, remove bin entry --- crates/astria-auctioneer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 77e9b6ca58..8abb1e4e90 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -2,7 +2,7 @@ name = "astria-auctioneer" version = "0.0.1" edition = "2021" -rust-version = "1.76" +rust-version = "1.81" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/astriaorg/astria" From f00c9fb191f60d11fd5e5140498cfbeb4cd77541 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 08:46:04 +0100 Subject: [PATCH 08/73] remove unused dependencies --- Cargo.lock | 9 --------- crates/astria-auctioneer/Cargo.toml | 15 --------------- crates/astria-auctioneer/src/bundle/client.rs | 6 ++++-- .../src/optimistic_executor/mod.rs | 2 +- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d33c6200b..13e4dfdc5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,31 +507,22 @@ dependencies = [ "astria-eyre", "astria-sequencer-client", "astria-telemetry", - "astria-test-utils", - "async-trait", - "axum", "bytes", "futures", "hex", "humantime", - "insta", "itertools 0.12.1", "pbjson-types", "pin-project-lite", "prost", "serde", "serde_json", - "sha2 0.10.8", - "tempfile", - "thiserror", "tokio", "tokio-stream", - "tokio-test", "tokio-util 0.7.12", "tonic 0.10.2", "tracing", "tryhard", - "wiremock", ] [[package]] diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 8abb1e4e90..e878785159 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -8,9 +8,6 @@ readme = "README.md" repository = "https://github.com/astriaorg/astria" homepage = "https://astria.org" -[[bin]] -name = "astria-auctioneer" - [dependencies] astria-build-info = { path = "../astria-build-info", features = ["runtime"] } astria-core = { path = "../astria-core", features = ["serde", "client"] } @@ -21,8 +18,6 @@ telemetry = { package = "astria-telemetry", path = "../astria-telemetry", featur "display", ] } -async-trait = { workspace = true } -axum = { workspace = true } bytes = { workspace = true } futures = { workspace = true } hex = { workspace = true } @@ -33,8 +28,6 @@ pin-project-lite = { workspace = true } prost = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -sha2 = { workspace = true } -thiserror = { workspace = true } tokio = { workspace = true, features = [ "macros", "rt-multi-thread", @@ -49,17 +42,9 @@ tonic = { workspace = true } tokio-stream = { workspace = true, features = ["net"] } [dev-dependencies] -astria-core = { path = "../astria-core", features = ["client"] } config = { package = "astria-config", path = "../astria-config", features = [ "tests", ] } -insta = { workspace = true, features = ["json"] } -tempfile = { workspace = true } -test_utils = { package = "astria-test-utils", path = "../astria-test-utils", features = [ - "geth", -] } -tokio-test = { workspace = true } -wiremock = { workspace = true } [build-dependencies] astria-build-info = { path = "../astria-build-info", features = ["build"] } diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs index 2240b3aae5..6193ebcfc6 100644 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ b/crates/astria-auctioneer/src/bundle/client.rs @@ -13,12 +13,14 @@ use astria_eyre::eyre::{ OptionExt, WrapErr as _, }; -use axum::http::Uri; use futures::{ Stream, StreamExt, }; -use tonic::transport::Endpoint; +use tonic::transport::{ + Endpoint, + Uri, +}; use tracing::{ instrument, warn, diff --git a/crates/astria-auctioneer/src/optimistic_executor/mod.rs b/crates/astria-auctioneer/src/optimistic_executor/mod.rs index 8eeed8e3d4..e652924028 100644 --- a/crates/astria-auctioneer/src/optimistic_executor/mod.rs +++ b/crates/astria-auctioneer/src/optimistic_executor/mod.rs @@ -38,9 +38,9 @@ use astria_eyre::eyre::{ OptionExt, WrapErr as _, }; +use futures::StreamExt as _; use telemetry::display::base64; use tokio::select; -use tokio_stream::StreamExt as _; use tokio_util::sync::CancellationToken; use tracing::{ error, From 15fe94ac2df502847080c1eb40a7bd01e78707eb Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 11:28:42 +0100 Subject: [PATCH 09/73] clean up logging in main, ensure root spans exist --- .../astria-auctioneer/src/auction/manager.rs | 4 +- .../astria-auctioneer/src/auctioneer/inner.rs | 6 +- .../astria-auctioneer/src/auctioneer/mod.rs | 32 +++++----- crates/astria-auctioneer/src/lib.rs | 2 +- crates/astria-auctioneer/src/main.rs | 58 +++++++++++++------ 5 files changed, 63 insertions(+), 39 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index e85f6f3f1f..c587196711 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -30,7 +30,7 @@ use super::{ Id, SequencerKey, }; -use crate::flatten_result; +use crate::flatten_join_result; pub(crate) struct Builder { pub(crate) metrics: &'static crate::Metrics, @@ -194,7 +194,7 @@ impl Manager { .remove(&auction_id) .expect("handle should always exist for running auction"); - Some((auction_id, flatten_result(result))) + Some((auction_id, flatten_join_result(result))) } else { None } diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index e2da31f057..b687a23fc2 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -21,7 +21,7 @@ use tracing::{ use crate::{ auction, - flatten_result, + flatten_join_result, optimistic_executor, Config, Metrics, @@ -111,7 +111,7 @@ impl Auctioneer { }, Some((name, res)) = self.tasks.join_next() => { - flatten_result(res) + flatten_join_result(res) .wrap_err_with(|| format!("task `{name}` failed")) .map(|()| "task `{name}` exited unexpectedly") } @@ -133,7 +133,7 @@ impl Auctioneer { let shutdown_loop = async { while let Some((name, res)) = self.tasks.join_next().await { let message = "task shut down"; - match flatten_result(res) { + match flatten_join_result(res) { Ok(()) => { info!(name, message); } diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index cce3a43586..3d18141bd9 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -1,17 +1,18 @@ -use std::future::Future; +use std::{ + future::Future, + task::Poll, +}; use astria_eyre::eyre::{ self, }; use pin_project_lite::pin_project; -use tokio::task::{ - JoinError, - JoinHandle, -}; +use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; use tracing::instrument; use crate::{ + flatten_join_result, Config, Metrics, }; @@ -51,22 +52,21 @@ impl Auctioneer { /// # Panics /// Panics if shutdown is called twice. #[instrument(skip_all, err)] - pub async fn shutdown(&mut self) -> Result, JoinError> { + pub async fn shutdown(&mut self) -> eyre::Result<()> { self.shutdown_token.cancel(); - self.task - .take() - .expect("shutdown must not be called twice") - .await + flatten_join_result( + self.task + .take() + .expect("shutdown must not be called twice") + .await, + ) } } impl Future for Auctioneer { - type Output = Result, tokio::task::JoinError>; + type Output = eyre::Result<()>; - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { use futures::future::FutureExt as _; let this = self.project(); @@ -74,6 +74,6 @@ impl Future for Auctioneer { .task .as_mut() .expect("the Auctioneer handle must not be polled after shutdown"); - task.poll_unpin(cx) + task.poll_unpin(cx).map(flatten_join_result) } } diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index b50e8078b7..62bddd6e90 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -23,7 +23,7 @@ pub use metrics::Metrics; pub use telemetry; use tokio::task::JoinError; -fn flatten_result(res: Result, JoinError>) -> eyre::Result { +fn flatten_join_result(res: Result, JoinError>) -> eyre::Result { match res { Ok(Ok(val)) => Ok(val), Ok(Err(err)) => Err(err).wrap_err("task returned with error"), diff --git a/crates/astria-auctioneer/src/main.rs b/crates/astria-auctioneer/src/main.rs index c6d9ea0db0..7032e4477b 100644 --- a/crates/astria-auctioneer/src/main.rs +++ b/crates/astria-auctioneer/src/main.rs @@ -5,7 +5,11 @@ use astria_auctioneer::{ Config, BUILD_INFO, }; -use astria_eyre::eyre::WrapErr as _; +use astria_eyre::eyre::{ + self, + eyre, + WrapErr as _, +}; use tokio::{ select, signal::unix::{ @@ -16,6 +20,7 @@ use tokio::{ use tracing::{ error, info, + instrument, warn, }; @@ -25,8 +30,17 @@ async fn main() -> ExitCode { eprintln!("{}", telemetry::display::json(&BUILD_INFO)); - let cfg: Config = config::get().expect("failed to read configuration"); - eprintln!("{}", telemetry::display::json(&cfg),); + let cfg: Config = match config::get() { + Err(err) => { + eprintln!("failed to read configuration:\n{err:?}"); + return ExitCode::FAILURE; + } + Ok(cfg) => cfg, + }; + eprintln!( + "starting with configuration:\n{}", + telemetry::display::json(&cfg), + ); let mut telemetry_conf = telemetry::configure() .set_no_otel(cfg.no_otel) @@ -66,22 +80,32 @@ async fn main() -> ExitCode { let mut sigterm = signal(SignalKind::terminate()) .expect("setting a SIGTERM listener should always work on Unix"); - select! { - _ = sigterm.recv() => { - info!("received SIGTERM; shutting down"); - if let Err(error) = auctioneer.shutdown().await { - warn!(%error, "encountered an error while shutting down"); - } - info!("auctioneer stopped"); - ExitCode::SUCCESS + let exit_reason = select! { + _ = sigterm.recv() => Ok("received shutdown signal"), + res = &mut auctioneer => { + res.and_then(|()| Err(eyre!("auctioneer task exited unexpectedly"))) } + }; - res = &mut auctioneer => { - error!( - error = res.err().map(tracing::field::display), - "auctioneer task exited unexpectedly" - ); + shutdown(exit_reason, auctioneer).await +} + +#[instrument(skip_all)] +async fn shutdown(reason: eyre::Result<&'static str>, mut service: Auctioneer) -> ExitCode { + let message = "shutting down"; + let exit_code = match reason { + Ok(reason) => { + info!(reason, message); + if let Err(error) = service.shutdown().await { + warn!(%error, "encountered errors during shutdown") + }; + ExitCode::SUCCESS + } + Err(reason) => { + error!(%reason, message); ExitCode::FAILURE } - } + }; + info!("shutdown target reached"); + exit_code } From b9ea05e86086cadedb462c7c198985523e062df4 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 11:48:04 +0100 Subject: [PATCH 10/73] remove unused associated const --- crates/astria-auctioneer/src/auctioneer/inner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index b687a23fc2..73501af1ae 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -37,7 +37,6 @@ pub(super) struct Auctioneer { impl Auctioneer { const OPTIMISTIC_EXECUTOR: &'static str = "optimistic_executor"; - const _BUNDLE_COLLECTOR: &'static str = "bundle_collector"; /// Creates an [`Auctioneer`] service from a [`Config`] and [`Metrics`]. pub(super) fn new( From b4c0577ebac3b69fd6d94be1e005bb290c801656 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 12:47:44 +0100 Subject: [PATCH 11/73] rename auctioneer::inner::Auctioneer -> auctioneer::Inner and fix some doc comments --- crates/astria-auctioneer/src/auctioneer/inner.rs | 5 +++-- crates/astria-auctioneer/src/auctioneer/mod.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index 73501af1ae..b994115a4e 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -27,7 +27,8 @@ use crate::{ Metrics, }; -pub(super) struct Auctioneer { +/// The implementation of the auctioneer business logic. +pub(super) struct Inner { /// Used to signal the service to shutdown shutdown_token: CancellationToken, @@ -35,7 +36,7 @@ pub(super) struct Auctioneer { tasks: JoinMap<&'static str, eyre::Result<()>>, } -impl Auctioneer { +impl Inner { const OPTIMISTIC_EXECUTOR: &'static str = "optimistic_executor"; /// Creates an [`Auctioneer`] service from a [`Config`] and [`Metrics`]. diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index 3d18141bd9..2016fcc950 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -6,6 +6,7 @@ use std::{ use astria_eyre::eyre::{ self, }; +use inner::Inner; use pin_project_lite::pin_project; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; @@ -20,7 +21,7 @@ use crate::{ mod inner; pin_project! { - /// Handle to the [`Auctioneer`] service, returned by [`Auctioneer::spawn`]. + /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. pub struct Auctioneer { shutdown_token: CancellationToken, task: Option>>, @@ -28,14 +29,13 @@ pin_project! { } impl Auctioneer { - /// Creates an [`Auctioneer`] service and runs it, returning a handle to the taks and shutdown - /// token. + /// Spawns the [`Auctioneer`] service. /// /// # Errors /// Returns an error if the Auctioneer cannot be initialized. pub fn spawn(cfg: Config, metrics: &'static Metrics) -> eyre::Result { let shutdown_token = CancellationToken::new(); - let inner = inner::Auctioneer::new(cfg, metrics, shutdown_token.child_token())?; + let inner = Inner::new(cfg, metrics, shutdown_token.child_token())?; let task = tokio::spawn(inner.run()); Ok(Self { @@ -44,13 +44,13 @@ impl Auctioneer { }) } - /// Initiates shutdown of the Auctioneer and returns its result. + /// Shuts down Auctioneer, in turn waiting for its components to shut down. /// /// # Errors - /// Returns an error if the Auctioneer exited with an error. + /// Returns an error if an error occured during shutdown. /// /// # Panics - /// Panics if shutdown is called twice. + /// Panics if called twice. #[instrument(skip_all, err)] pub async fn shutdown(&mut self) -> eyre::Result<()> { self.shutdown_token.cancel(); From 9a33b04b051d6a2f0b95a693ec20158e65491e41 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 22:03:50 +0100 Subject: [PATCH 12/73] have bundle stream match other stream impls --- crates/astria-auctioneer/src/bundle/client.rs | 32 +++++++++++++------ crates/astria-auctioneer/src/bundle/mod.rs | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs index 6193ebcfc6..5e81d284d6 100644 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ b/crates/astria-auctioneer/src/bundle/client.rs @@ -10,13 +10,14 @@ use astria_core::generated::bundle::v1alpha1::{ }; use astria_eyre::eyre::{ self, - OptionExt, + eyre, WrapErr as _, }; use futures::{ Stream, StreamExt, }; +use prost::Name; use tonic::transport::{ Endpoint, Uri, @@ -99,8 +100,11 @@ fn make_retry_cfg( ) } -pub(crate) struct BundleStream { - client: Pin>>, +pin_project_lite::pin_project! { + pub(crate) struct BundleStream { + #[pin] + inner: tonic::Streaming, + } } impl BundleStream { @@ -113,7 +117,7 @@ impl BundleStream { .wrap_err("failed to get bundle stream")?; Ok(Self { - client: Box::pin(stream), + inner: stream, }) } } @@ -125,16 +129,26 @@ impl Stream for BundleStream { mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.client.poll_next_unpin(cx)) else { + let Some(res) = futures::ready!(self.inner.poll_next_unpin(cx)) else { return std::task::Poll::Ready(None); }; let raw = res - .wrap_err("received gRPC error")? + .wrap_err("failed receiving streamed message from server")? .bundle - .ok_or_eyre("bundle stream response did not contain bundle")?; - - let bundle = Bundle::try_from_raw(raw).wrap_err("failed to parse raw Bundle")?; + .ok_or_else(|| { + eyre!( + "message field not set: `{}.bundle`", + GetBundleStreamResponse::full_name() + ) + })?; + + let bundle = Bundle::try_from_raw(raw).wrap_err_with(|| { + format!( + "failed to validate received `{}`", + astria_core::generated::bundle::v1alpha1::Bundle::full_name() + ) + })?; std::task::Poll::Ready(Some(Ok(bundle))) } diff --git a/crates/astria-auctioneer/src/bundle/mod.rs b/crates/astria-auctioneer/src/bundle/mod.rs index cc77285350..946adcf841 100644 --- a/crates/astria-auctioneer/src/bundle/mod.rs +++ b/crates/astria-auctioneer/src/bundle/mod.rs @@ -43,7 +43,7 @@ pub(crate) struct Bundle { } impl Bundle { - fn try_from_raw(raw: raw::Bundle) -> eyre::Result { + pub(crate) fn try_from_raw(raw: raw::Bundle) -> eyre::Result { let raw::Bundle { fee, transactions, From 5e63066a7192e9bc608e55a0563c2a7fc80bd72c Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 21 Nov 2024 22:05:12 +0100 Subject: [PATCH 13/73] import traits as _ --- crates/astria-auctioneer/src/block/executed_stream.rs | 2 +- crates/astria-auctioneer/src/bundle/client.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/astria-auctioneer/src/block/executed_stream.rs b/crates/astria-auctioneer/src/block/executed_stream.rs index bea5db6ed4..33a58ccf92 100644 --- a/crates/astria-auctioneer/src/block/executed_stream.rs +++ b/crates/astria-auctioneer/src/block/executed_stream.rs @@ -13,7 +13,7 @@ use astria_eyre::eyre::{ }; use futures::{ Stream, - StreamExt, + StreamExt as _, }; use pin_project_lite::pin_project; use telemetry::display::base64; diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs index 5e81d284d6..283d409d23 100644 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ b/crates/astria-auctioneer/src/bundle/client.rs @@ -15,7 +15,7 @@ use astria_eyre::eyre::{ }; use futures::{ Stream, - StreamExt, + StreamExt as _, }; use prost::Name; use tonic::transport::{ From 93a2d1ecc1c14db881f4a8c422b9e0f0fd1f2cb6 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 22 Nov 2024 06:47:55 +0100 Subject: [PATCH 14/73] remove explicit retries for opening streams; rely on tonic's reconnection logic --- crates/astria-auctioneer/src/bundle/client.rs | 60 ++----------- .../src/optimistic_block_client.rs | 84 ++++--------------- .../src/optimistic_execution_client.rs | 65 ++------------ 3 files changed, 31 insertions(+), 178 deletions(-) diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs index 283d409d23..67f15be3c3 100644 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ b/crates/astria-auctioneer/src/bundle/client.rs @@ -1,7 +1,4 @@ -use std::{ - pin::Pin, - time::Duration, -}; +use std::pin::Pin; use astria_core::generated::bundle::v1alpha1::{ bundle_service_client::BundleServiceClient, @@ -22,13 +19,7 @@ use tonic::transport::{ Endpoint, Uri, }; -use tracing::{ - instrument, - warn, - Instrument, - Span, -}; -use tryhard::backoff_strategies::ExponentialBackoff; +use tracing::instrument; use super::Bundle; @@ -55,51 +46,16 @@ impl BundleClient { pub(crate) async fn get_bundle_stream( &mut self, ) -> eyre::Result> { - let span = tracing::Span::current(); - let retry_cfg = make_retry_cfg("get bundle stream".into(), span); - let client = self.inner.clone(); - - let stream = tryhard::retry_fn(|| { - let mut client = client.clone(); - async move { client.get_bundle_stream(GetBundleStreamRequest {}).await } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to get bundle stream")? - .into_inner(); - + let mut client = self.inner.clone(); + let stream = client + .get_bundle_stream(GetBundleStreamRequest {}) + .await + .wrap_err("failed to open bundle stream")? + .into_inner(); Ok(stream) } } -fn make_retry_cfg( - msg: String, - span: Span, -) -> tryhard::RetryFutureConfig< - ExponentialBackoff, - impl Fn(u32, Option, &tonic::Status) -> futures::future::Ready<()>, -> { - tryhard::RetryFutureConfig::new(1024) - .exponential_backoff(Duration::from_millis(100)) - .max_delay(Duration::from_secs(2)) - .on_retry( - move |attempt: u32, next_delay: Option, error: &tonic::Status| { - let wait_duration = next_delay - .map(humantime::format_duration) - .map(tracing::field::display); - warn!( - parent: &span, - attempt, - wait_duration, - error = error as &dyn std::error::Error, - "attempt to {msg} failed; retrying after backoff", - ); - futures::future::ready(()) - }, - ) -} - pin_project_lite::pin_project! { pub(crate) struct BundleStream { #[pin] diff --git a/crates/astria-auctioneer/src/optimistic_block_client.rs b/crates/astria-auctioneer/src/optimistic_block_client.rs index 7a2cacc96d..ffda5ff978 100644 --- a/crates/astria-auctioneer/src/optimistic_block_client.rs +++ b/crates/astria-auctioneer/src/optimistic_block_client.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use astria_core::{ generated::sequencerblock::optimisticblock::v1alpha1::{ optimistic_block_service_client::OptimisticBlockServiceClient, @@ -19,15 +17,8 @@ use tonic::transport::{ Endpoint, Uri, }; -use tracing::{ - instrument, - warn, - Instrument as _, - Span, -}; -use tryhard::backoff_strategies::ExponentialBackoff; +use tracing::instrument; -/// Wraps the gRPC client for the Sequencer service that wraps client calls with `tryhard`. #[derive(Debug, Clone)] pub(crate) struct OptimisticBlockClient { inner: OptimisticBlockServiceClient, @@ -49,7 +40,6 @@ impl OptimisticBlockClient { }) } - // TODO: this should probably be separated from the tryhard logic and put in an extension trait #[instrument(skip_all, fields( uri = %self.uri, %rollup_id, @@ -59,27 +49,17 @@ impl OptimisticBlockClient { &mut self, rollup_id: RollupId, ) -> eyre::Result> { - let span = tracing::Span::current(); - let retry_cfg = make_retry_cfg("stream optimistic blocks".into(), span); - let client = self.inner.clone(); - - let stream = tryhard::retry_fn(|| { - let mut client = client.clone(); - let req = GetOptimisticBlockStreamRequest { + let stream = self + .inner + .get_optimistic_block_stream(GetOptimisticBlockStreamRequest { rollup_id: Some(rollup_id.into_raw()), - }; - async move { client.get_optimistic_block_stream(req).await } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to initialize optimistic block stream")? - .into_inner(); - + }) + .await + .wrap_err("failed to open optimistic block stream")? + .into_inner(); Ok(stream) } - // TODO: this should probably be separated from the tryhard logic and put in an extension trait #[instrument(skip_all, fields( uri = %self.uri, err, @@ -87,48 +67,12 @@ impl OptimisticBlockClient { pub(super) async fn get_block_commitment_stream( &mut self, ) -> eyre::Result> { - let span = tracing::Span::current(); - let retry_cfg = make_retry_cfg("stream block commitments".into(), span); - let client = self.inner.clone(); - - let stream = tryhard::retry_fn(|| { - let mut client = client.clone(); - let req = GetBlockCommitmentStreamRequest {}; - async move { client.get_block_commitment_stream(req).await } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to initialize block commitment stream")? - .into_inner(); - + let stream = self + .inner + .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) + .await + .wrap_err("failed to open block commitment stream")? + .into_inner(); Ok(stream) } } - -fn make_retry_cfg( - msg: String, - span: Span, -) -> tryhard::RetryFutureConfig< - ExponentialBackoff, - impl Fn(u32, Option, &tonic::Status) -> futures::future::Ready<()>, -> { - tryhard::RetryFutureConfig::new(1024) - .exponential_backoff(Duration::from_millis(100)) - .max_delay(Duration::from_secs(2)) - .on_retry( - move |attempt: u32, next_delay: Option, error: &tonic::Status| { - let wait_duration = next_delay - .map(humantime::format_duration) - .map(tracing::field::display); - warn!( - parent: &span, - attempt, - wait_duration, - error = error as &dyn std::error::Error, - "attempt to {msg} failed; retrying after backoff", - ); - futures::future::ready(()) - }, - ) -} diff --git a/crates/astria-auctioneer/src/optimistic_execution_client.rs b/crates/astria-auctioneer/src/optimistic_execution_client.rs index d836a41451..cac04be784 100644 --- a/crates/astria-auctioneer/src/optimistic_execution_client.rs +++ b/crates/astria-auctioneer/src/optimistic_execution_client.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use astria_core::{ generated::bundle::v1alpha1::{ optimistic_execution_service_client::OptimisticExecutionServiceClient, @@ -17,13 +15,7 @@ use tonic::transport::{ Endpoint, Uri, }; -use tracing::{ - instrument, - warn, - Instrument, - Span, -}; -use tryhard::backoff_strategies::ExponentialBackoff; +use tracing::instrument; use crate::block::{ self, @@ -63,52 +55,13 @@ impl OptimisticExecutionClient { tonic::Streaming, mpsc::Sender, )> { - let span = tracing::Span::current(); - let retry_cfg = make_retry_cfg("execute optimistic blocks".into(), span); - let client = self.inner.clone(); - - let (stream, opt_tx) = tryhard::retry_fn(|| { - let mut client = client.clone(); - - let (blocks_to_execute_tx, requests) = make_execution_requests_stream(rollup_id); - - async move { - let stream = client.execute_optimistic_block_stream(requests).await?; - Ok((stream, blocks_to_execute_tx)) - } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to initialize optimistic execution stream")?; - - Ok((stream.into_inner(), opt_tx)) + let (blocks_to_execute_tx, requests) = make_execution_requests_stream(rollup_id); + let stream = self + .inner + .execute_optimistic_block_stream(requests) + .await + .wrap_err("failed to open execute optimistic block stream")? + .into_inner(); + Ok((stream, blocks_to_execute_tx)) } } - -fn make_retry_cfg( - msg: String, - span: Span, -) -> tryhard::RetryFutureConfig< - ExponentialBackoff, - impl Fn(u32, Option, &tonic::Status) -> futures::future::Ready<()>, -> { - tryhard::RetryFutureConfig::new(1024) - .exponential_backoff(Duration::from_millis(100)) - .max_delay(Duration::from_secs(2)) - .on_retry( - move |attempt: u32, next_delay: Option, error: &tonic::Status| { - let wait_duration = next_delay - .map(humantime::format_duration) - .map(tracing::field::display); - warn!( - parent: &span, - attempt, - wait_duration, - error = error as &dyn std::error::Error, - "attempt to {msg} failed; retrying after backoff", - ); - futures::future::ready(()) - }, - ) -} From 92b22237d4e3ad7d6aa4e04300442cc42dda9dc3 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 22 Nov 2024 14:33:21 +0100 Subject: [PATCH 15/73] core: impl Protobuf for FilteredSequencerBlock --- .../src/sequencerblock/v1/block.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/astria-core/src/sequencerblock/v1/block.rs b/crates/astria-core/src/sequencerblock/v1/block.rs index 48f8907fac..6b657b72b6 100644 --- a/crates/astria-core/src/sequencerblock/v1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1/block.rs @@ -37,7 +37,7 @@ use crate::{ Transaction, TransactionError, }, - Protobuf as _, + Protobuf, }; #[derive(Debug, thiserror::Error)] @@ -1269,6 +1269,27 @@ impl FilteredSequencerBlock { } } +impl Protobuf for FilteredSequencerBlock { + type Error = FilteredSequencerBlockError; + type Raw = raw::FilteredSequencerBlock; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + Self::try_from_raw(raw.clone()) + } + + fn try_from_raw(raw: Self::Raw) -> Result { + Self::try_from_raw(raw) + } + + fn to_raw(&self) -> Self::Raw { + self.clone().into_raw() + } + + fn into_raw(self) -> Self::Raw { + self.into_raw() + } +} + #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct FilteredSequencerBlockError(FilteredSequencerBlockErrorKind); From 3d47389b7780867b76db1f8b1ee328e585b3fdf6 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 22 Nov 2024 14:34:00 +0100 Subject: [PATCH 16/73] run sequencer streams off of a single channel the intended way to communicate with a single server is by multiplexing over one connection. this also modifies the executed stream to not also forward something to another stream (happening outside of it, in the optimistic executor broker) --- .../astria-auctioneer/src/auctioneer/inner.rs | 3 +- .../src/block/commitment_stream.rs | 64 ------- .../src/block/executed_stream.rs | 10 +- crates/astria-auctioneer/src/block/mod.rs | 18 +- .../src/block/optimistic_stream.rs | 96 ---------- crates/astria-auctioneer/src/lib.rs | 2 +- .../src/optimistic_block_client.rs | 78 -------- .../src/optimistic_executor/builder.rs | 19 +- .../src/optimistic_executor/mod.rs | 48 +++-- .../src/sequencer_channel.rs | 176 ++++++++++++++++++ 10 files changed, 236 insertions(+), 278 deletions(-) delete mode 100644 crates/astria-auctioneer/src/block/commitment_stream.rs delete mode 100644 crates/astria-auctioneer/src/block/optimistic_stream.rs delete mode 100644 crates/astria-auctioneer/src/optimistic_block_client.rs create mode 100644 crates/astria-auctioneer/src/sequencer_channel.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index b994115a4e..4ee1e460e9 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -83,7 +83,8 @@ impl Inner { rollup_grpc_endpoint, auctions, } - .build(); + .build() + .wrap_err("failed to construct optimistic executor")?; tasks.spawn(Self::OPTIMISTIC_EXECUTOR, async { optimistic_executor diff --git a/crates/astria-auctioneer/src/block/commitment_stream.rs b/crates/astria-auctioneer/src/block/commitment_stream.rs deleted file mode 100644 index aa603ab074..0000000000 --- a/crates/astria-auctioneer/src/block/commitment_stream.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::pin::Pin; - -use astria_core::generated::sequencerblock::optimisticblock::v1alpha1::GetBlockCommitmentStreamResponse; -use astria_eyre::eyre::{ - self, - Context, - OptionExt as _, -}; -use futures::{ - Stream, - StreamExt as _, -}; -use pin_project_lite::pin_project; -use telemetry::display::base64; -use tracing::debug; - -use super::Commitment; -use crate::optimistic_block_client::OptimisticBlockClient; - -pin_project! { - /// A stream for receiving committed blocks from the sequencer. - pub(crate) struct BlockCommitmentStream { - #[pin] - client: tonic::Streaming, - } -} - -impl BlockCommitmentStream { - pub(crate) async fn connect(mut sequencer_client: OptimisticBlockClient) -> eyre::Result { - let commitment_stream_client = sequencer_client - .get_block_commitment_stream() - .await - .wrap_err("failed to stream block commitments")?; - - Ok(Self { - client: commitment_stream_client, - }) - } -} - -impl Stream for BlockCommitmentStream { - type Item = eyre::Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.client.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); - }; - - let raw = res - .wrap_err("received gRPC error")? - .commitment - .ok_or_eyre("response did not contain block commitment")?; - - let commitment = - Commitment::try_from_raw(&raw).wrap_err("failed to parse raw to BlockCommitment")?; - - debug!(block_commitment.sequencer_block_hash = %base64(&commitment.sequencer_block_hash()), "received block commitment"); - - std::task::Poll::Ready(Some(Ok(commitment))) - } -} diff --git a/crates/astria-auctioneer/src/block/executed_stream.rs b/crates/astria-auctioneer/src/block/executed_stream.rs index 33a58ccf92..ff93d0b71d 100644 --- a/crates/astria-auctioneer/src/block/executed_stream.rs +++ b/crates/astria-auctioneer/src/block/executed_stream.rs @@ -6,6 +6,7 @@ use astria_core::{ ExecuteOptimisticBlockStreamResponse, }, primitive::v1::RollupId, + sequencerblock::v1::block::FilteredSequencerBlock, }; use astria_eyre::eyre::{ self, @@ -35,9 +36,14 @@ pub(crate) struct Handle { } impl Handle { - pub(crate) fn try_send_block_to_execute(&mut self, block: Optimistic) -> eyre::Result<()> { + pub(crate) fn try_send_block_to_execute( + &mut self, + filtered_sequencer_block: FilteredSequencerBlock, + ) -> eyre::Result<()> { self.blocks_to_execute_tx - .try_send(block) + .try_send(Optimistic { + filtered_sequencer_block, + }) .wrap_err("failed to send block to execute")?; Ok(()) diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index 3d087241de..f331ffa754 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -31,9 +31,7 @@ use telemetry::display::base64; use crate::bundle::Bundle; -pub(crate) mod commitment_stream; pub(crate) mod executed_stream; -pub(crate) mod optimistic_stream; /// Converts a [`tendermint::Time`] to a [`prost_types::Timestamp`]. fn convert_tendermint_time_to_protobuf_timestamp( @@ -56,14 +54,6 @@ pub(crate) struct Optimistic { } impl Optimistic { - pub(crate) fn try_from_raw( - raw: raw_sequencer_block::FilteredSequencerBlock, - ) -> eyre::Result { - Ok(Self { - filtered_sequencer_block: FilteredSequencerBlock::try_from_raw(raw)?, - }) - } - /// Converts this [`Optimistic`] into a [`BaseBlock`] for the given `rollup_id`. /// If there are no transactions for the given `rollup_id`, this will return a `BaseBlock` /// with no transactions. @@ -166,6 +156,8 @@ impl Executed { } #[derive(Debug, Clone)] +// FIXME: This is called a `Commitment` but is produced from a `SequencerBlockCommit`. +// This is very confusing. pub(crate) struct Commitment { /// The height of the sequencer block that was committed. sequencer_height: u64, @@ -204,9 +196,11 @@ pub(crate) struct Current { impl Current { /// Creates a new `Current` with the given `optimistic_block`. - pub(crate) fn with_optimistic(optimistic_block: Optimistic) -> Self { + pub(crate) fn with_optimistic(filtered_sequencer_block: FilteredSequencerBlock) -> Self { Self { - optimistic: optimistic_block, + optimistic: Optimistic { + filtered_sequencer_block, + }, executed: None, commitment: None, } diff --git a/crates/astria-auctioneer/src/block/optimistic_stream.rs b/crates/astria-auctioneer/src/block/optimistic_stream.rs deleted file mode 100644 index 76c384dd21..0000000000 --- a/crates/astria-auctioneer/src/block/optimistic_stream.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::pin::Pin; - -use astria_core::{ - generated::sequencerblock::optimisticblock::v1alpha1::GetOptimisticBlockStreamResponse, - primitive::v1::RollupId, -}; -use astria_eyre::eyre::{ - self, - Context, - OptionExt as _, -}; -use futures::{ - Stream, - StreamExt as _, -}; -use pin_project_lite::pin_project; -use telemetry::display::base64; -use tracing::debug; - -use super::{ - executed_stream, - Optimistic, -}; -use crate::optimistic_block_client::OptimisticBlockClient; - -pin_project! { - /// A stream for receiving optimistic blocks from the sequencer. - /// Blocks received optimistically will be checked for validity and a clone will - /// be sent to the rollup's optimistic execution serivce before returning them - /// for further processing. - /// - /// ## Backpressure - /// While blocks are forwarded using an `mpsc` channel, we only receive incoming - /// optimistic blocks from the sequencer when CometBFT proposals are processed. - /// Multiple optimsitic blocks will be received in a short amount of time only in - /// the event that CometBFT receives multiple proposals within a single block's slot. - /// We assume this is relatively rare and that under normal operations a block will - /// be sent optimistically once per slot (~2 seconds). - pub(crate) struct OptimisticBlockStream { - #[pin] - client: tonic::Streaming, - #[pin] - execution_handle: executed_stream::Handle, - } -} - -impl OptimisticBlockStream { - pub(crate) async fn connect( - rollup_id: RollupId, - mut sequencer_client: OptimisticBlockClient, - execution_handle: executed_stream::Handle, - ) -> eyre::Result { - let optimistic_stream_client = sequencer_client - .get_optimistic_block_stream(rollup_id) - .await - .wrap_err("failed to stream optimistic blocks")?; - - Ok(OptimisticBlockStream { - client: optimistic_stream_client, - execution_handle, - }) - } -} - -impl Stream for OptimisticBlockStream { - type Item = eyre::Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context, - ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.client.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); - }; - - let raw = res - .wrap_err("received gRPC error")? - .block - .ok_or_eyre("response did not contain filtered sequencer block")?; - - let optimistic_block = - Optimistic::try_from_raw(raw).wrap_err("failed to parse raw to Optimistic")?; - - debug!( - optimistic_block.sequencer_block_hash = %base64(optimistic_block.sequencer_block_hash()), - "received optimistic block from sequencer" - ); - - // forward the optimistic block to the rollup's optimistic execution server - self.execution_handle - .try_send_block_to_execute(optimistic_block.clone()) - .wrap_err("failed to send optimistic block for execution")?; - - std::task::Poll::Ready(Some(Ok(optimistic_block))) - } -} diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index 62bddd6e90..fc1cd6a00a 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -7,9 +7,9 @@ mod build_info; mod bundle; pub mod config; pub(crate) mod metrics; -mod optimistic_block_client; mod optimistic_execution_client; mod optimistic_executor; +mod sequencer_channel; mod sequencer_key; use astria_eyre::{ diff --git a/crates/astria-auctioneer/src/optimistic_block_client.rs b/crates/astria-auctioneer/src/optimistic_block_client.rs deleted file mode 100644 index ffda5ff978..0000000000 --- a/crates/astria-auctioneer/src/optimistic_block_client.rs +++ /dev/null @@ -1,78 +0,0 @@ -use astria_core::{ - generated::sequencerblock::optimisticblock::v1alpha1::{ - optimistic_block_service_client::OptimisticBlockServiceClient, - GetBlockCommitmentStreamRequest, - GetBlockCommitmentStreamResponse, - GetOptimisticBlockStreamRequest, - GetOptimisticBlockStreamResponse, - }, - primitive::v1::RollupId, -}; -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use tonic::transport::{ - Channel, - Endpoint, - Uri, -}; -use tracing::instrument; - -#[derive(Debug, Clone)] -pub(crate) struct OptimisticBlockClient { - inner: OptimisticBlockServiceClient, - uri: Uri, -} - -impl OptimisticBlockClient { - pub(crate) fn new(sequencer_uri: &str) -> eyre::Result { - let uri = sequencer_uri - .parse::() - .wrap_err("failed parsing provided string as Uri")?; - - // TODO: use a UDS socket instead - let endpoint = Endpoint::from(uri.clone()); - let inner = OptimisticBlockServiceClient::new(endpoint.connect_lazy()); - Ok(Self { - inner, - uri, - }) - } - - #[instrument(skip_all, fields( - uri = %self.uri, - %rollup_id, - err, - ))] - pub(super) async fn get_optimistic_block_stream( - &mut self, - rollup_id: RollupId, - ) -> eyre::Result> { - let stream = self - .inner - .get_optimistic_block_stream(GetOptimisticBlockStreamRequest { - rollup_id: Some(rollup_id.into_raw()), - }) - .await - .wrap_err("failed to open optimistic block stream")? - .into_inner(); - Ok(stream) - } - - #[instrument(skip_all, fields( - uri = %self.uri, - err, - ))] - pub(super) async fn get_block_commitment_stream( - &mut self, - ) -> eyre::Result> { - let stream = self - .inner - .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) - .await - .wrap_err("failed to open block commitment stream")? - .into_inner(); - Ok(stream) - } -} diff --git a/crates/astria-auctioneer/src/optimistic_executor/builder.rs b/crates/astria-auctioneer/src/optimistic_executor/builder.rs index 8dce746aa0..46c515328c 100644 --- a/crates/astria-auctioneer/src/optimistic_executor/builder.rs +++ b/crates/astria-auctioneer/src/optimistic_executor/builder.rs @@ -1,9 +1,14 @@ use astria_core::primitive::v1::RollupId; +use astria_eyre::eyre::{ + self, + WrapErr as _, +}; use tokio_util::sync::CancellationToken; use super::Startup; use crate::{ auction, + sequencer_channel::SequencerChannel, Metrics, }; @@ -23,7 +28,7 @@ pub(crate) struct Builder { } impl Builder { - pub(crate) fn build(self) -> Startup { + pub(crate) fn build(self) -> eyre::Result { let Self { metrics, shutdown_token, @@ -34,14 +39,20 @@ impl Builder { } = self; let rollup_id = RollupId::from_unhashed_bytes(&rollup_id); + let sequencer_channel = + SequencerChannel::create(&sequencer_grpc_endpoint).wrap_err_with(|| { + format!( + "failed to create a gRPC channel to Sequencer at `{sequencer_grpc_endpoint}`" + ) + })?; - Startup { + Ok(Startup { metrics, shutdown_token, - sequencer_grpc_endpoint, rollup_id, rollup_grpc_endpoint, + sequencer_channel, auctions, - } + }) } } diff --git a/crates/astria-auctioneer/src/optimistic_executor/mod.rs b/crates/astria-auctioneer/src/optimistic_executor/mod.rs index e652924028..1efa2d788f 100644 --- a/crates/astria-auctioneer/src/optimistic_executor/mod.rs +++ b/crates/astria-auctioneer/src/optimistic_executor/mod.rs @@ -31,7 +31,10 @@ //! We assume this is highly unlikely, as the rollup node's should filter the bundles it streams //! by its optimistic head block hash. -use astria_core::primitive::v1::RollupId; +use astria_core::{ + primitive::v1::RollupId, + sequencerblock::v1::block::FilteredSequencerBlock, +}; use astria_eyre::eyre::{ self, eyre, @@ -53,15 +56,17 @@ use crate::{ auction, block::{ self, - commitment_stream::BlockCommitmentStream, executed_stream::ExecutedBlockStream, - optimistic_stream::OptimisticBlockStream, }, bundle::{ Bundle, BundleStream, }, - optimistic_block_client::OptimisticBlockClient, + sequencer_channel::{ + BlockCommitmentStream, + OptimisticBlockStream, + SequencerChannel, + }, }; mod builder; @@ -80,7 +85,7 @@ pub(crate) struct Startup { #[allow(dead_code)] metrics: &'static crate::Metrics, shutdown_token: CancellationToken, - sequencer_grpc_endpoint: String, + sequencer_channel: SequencerChannel, rollup_id: RollupId, rollup_grpc_endpoint: String, auctions: auction::Manager, @@ -91,7 +96,7 @@ impl Startup { let Self { metrics, shutdown_token, - sequencer_grpc_endpoint, + mut sequencer_channel, rollup_id, rollup_grpc_endpoint, auctions, @@ -102,19 +107,17 @@ impl Startup { .await .wrap_err("failed to initialize executed block stream")?; - let sequencer_client = OptimisticBlockClient::new(&sequencer_grpc_endpoint) - .wrap_err("failed to initialize sequencer grpc client")?; - let mut optimistic_blocks = OptimisticBlockStream::connect( - rollup_id, - sequencer_client.clone(), - execution_stream_handle, - ) - .await - .wrap_err("failed to initialize optimsitic block stream")?; + let mut optimistic_blocks = sequencer_channel + .open_get_optimistic_block_stream(rollup_id) + .await + .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; - let block_commitments = BlockCommitmentStream::connect(sequencer_client) + // TODO: create a way to forward the optimistic blocks to the execution stream. + + let block_commitments = sequencer_channel + .open_get_block_commitment_stream() .await - .wrap_err("failed to initialize block commitment stream")?; + .wrap_err("opening stream to receive block commitments from sequencer failed")?; let bundle_stream = BundleStream::connect(rollup_grpc_endpoint) .await @@ -136,6 +139,7 @@ impl Startup { bundle_stream, auctions, current_block, + execution_stream_handle, }) } } @@ -151,6 +155,7 @@ pub(crate) struct Running { bundle_stream: BundleStream, auctions: auction::Manager, current_block: block::Current, + execution_stream_handle: crate::block::executed_stream::Handle, } impl Running { @@ -170,7 +175,6 @@ impl Running { res = self.optimistic_blocks.next() => { let res = break_for_closed_stream!(res, "optimistic block stream closed"); - let _ = self.handle_optimistic_block(res); }, @@ -207,7 +211,7 @@ impl Running { #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] fn handle_optimistic_block( &mut self, - optimistic_block: eyre::Result, + optimistic_block: eyre::Result, ) -> eyre::Result<()> { let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; @@ -218,7 +222,7 @@ impl Running { .wrap_err("failed to abort auction")?; info!( - optimistic_block.sequencer_block_hash = %base64(optimistic_block.sequencer_block_hash()), + optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), "received optimistic block, aborting old auction and starting new auction" ); @@ -227,6 +231,10 @@ impl Running { auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); self.auctions.new_auction(new_auction_id); + self.execution_stream_handle + .try_send_block_to_execute(optimistic_block) + .wrap_err("failed to forward block to execution stream")?; + Ok(()) } diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs new file mode 100644 index 0000000000..65129b392c --- /dev/null +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -0,0 +1,176 @@ +use std::{ + pin::Pin, + time::Duration, +}; + +use astria_core::{ + generated::sequencerblock::optimisticblock::v1alpha1::{ + GetBlockCommitmentStreamRequest, + GetBlockCommitmentStreamResponse, + GetOptimisticBlockStreamRequest, + GetOptimisticBlockStreamResponse, + }, + primitive::v1::RollupId, + sequencerblock::v1::block::FilteredSequencerBlock, + Protobuf as _, +}; +use astria_eyre::eyre::{ + self, + eyre, + WrapErr as _, +}; +use futures::{ + Stream, + StreamExt as _, +}; +use prost::Name; +use tonic::transport::Channel; + +use crate::block::Commitment; + +pub(crate) struct SequencerChannel { + inner: Channel, +} + +impl SequencerChannel { + pub(crate) fn create(uri: &str) -> eyre::Result { + let channel = Channel::from_shared(uri.to_string()) + .wrap_err("failed to open a channel to the provided uri")? + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(2)) + .connect_lazy(); + + Ok(Self { + inner: channel, + }) + } + + pub(crate) async fn open_get_block_commitment_stream( + &mut self, + ) -> eyre::Result { + use astria_core::generated::sequencerblock::optimisticblock::v1alpha1:: + optimistic_block_service_client::OptimisticBlockServiceClient; + let mut client = OptimisticBlockServiceClient::new(self.inner.clone()); + let stream = client + .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) + .await + .wrap_err("failed to open block commitment stream")? + .into_inner(); + Ok(BlockCommitmentStream::new(stream)) + } + + pub(crate) async fn open_get_optimistic_block_stream( + &mut self, + rollup_id: RollupId, + ) -> eyre::Result { + use astria_core::generated::sequencerblock::optimisticblock::v1alpha1::{ + optimistic_block_service_client::OptimisticBlockServiceClient, + GetOptimisticBlockStreamRequest, + }; + let mut client = OptimisticBlockServiceClient::new(self.inner.clone()); + let stream = client + .get_optimistic_block_stream(GetOptimisticBlockStreamRequest { + rollup_id: Some(rollup_id.into_raw()), + }) + .await + .wrap_err("failed to open optimistic block stream")? + .into_inner(); + Ok(OptimisticBlockStream::new(stream)) + } +} + +pin_project_lite::pin_project! { + /// A stream for receiving committed blocks from the sequencer. + pub(crate) struct BlockCommitmentStream { + #[pin] + inner: tonic::Streaming, + } +} + +impl BlockCommitmentStream { + fn new(inner: tonic::Streaming) -> Self { + Self { + inner, + } + } +} + +impl Stream for BlockCommitmentStream { + type Item = eyre::Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { + return std::task::Poll::Ready(None); + }; + + let raw = res + .wrap_err("failed receiving message over stream")? + .commitment + .ok_or_else(|| { + eyre!( + "expected field `{}.commitment` was not set", + GetBlockCommitmentStreamResponse::full_name() + ) + })?; + + use astria_core::generated::sequencerblock::optimisticblock::v1alpha1 as raw; + let commitment = Commitment::try_from_raw(&raw).wrap_err_with(|| { + format!( + "failed to validate message `{}` received from server", + raw::SequencerBlockCommit::full_name() + ) + })?; + + std::task::Poll::Ready(Some(Ok(commitment))) + } +} + +pin_project_lite::pin_project! { + pub(crate) struct OptimisticBlockStream { + #[pin] + inner: tonic::Streaming, + } +} + +impl OptimisticBlockStream { + fn new(inner: tonic::Streaming) -> Self { + Self { + inner, + } + } +} + +impl Stream for OptimisticBlockStream { + type Item = eyre::Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context, + ) -> std::task::Poll> { + let Some(res) = futures::ready!(self.inner.poll_next_unpin(cx)) else { + return std::task::Poll::Ready(None); + }; + + let raw = res + .wrap_err("failed receiving message over stream")? + .block + .ok_or_else(|| { + eyre!( + "expected field `{}.block` was not set", + GetOptimisticBlockStreamRequest::full_name() + ) + })?; + + let optimistic_block = FilteredSequencerBlock::try_from_raw(raw).wrap_err_with(|| { + format!( + "failed to validate `{}`", + FilteredSequencerBlock::full_name() + ) + })?; + + std::task::Poll::Ready(Some(Ok(optimistic_block))) + } +} From 195f25f9e46634329a1fc032d3b4a0a1ed890750 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 22 Nov 2024 14:52:29 +0100 Subject: [PATCH 17/73] remove needless pin projection all streams were Unpin anyways --- Cargo.lock | 1 - crates/astria-auctioneer/Cargo.toml | 1 - .../astria-auctioneer/src/auctioneer/mod.rs | 19 +++++++++---------- .../src/block/executed_stream.rs | 10 +++------- crates/astria-auctioneer/src/bundle/client.rs | 7 ++----- .../src/sequencer_channel.rs | 16 +++++----------- 6 files changed, 19 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13e4dfdc5b..20ab3837f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,7 +513,6 @@ dependencies = [ "humantime", "itertools 0.12.1", "pbjson-types", - "pin-project-lite", "prost", "serde", "serde_json", diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index e878785159..9ea944e461 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -24,7 +24,6 @@ hex = { workspace = true } humantime = { workspace = true } itertools = { workspace = true } pbjson-types = { workspace = true } -pin-project-lite = { workspace = true } prost = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index 2016fcc950..a868090e43 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -7,7 +7,6 @@ use astria_eyre::eyre::{ self, }; use inner::Inner; -use pin_project_lite::pin_project; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; use tracing::instrument; @@ -20,12 +19,10 @@ use crate::{ mod inner; -pin_project! { - /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. - pub struct Auctioneer { - shutdown_token: CancellationToken, - task: Option>>, - } +/// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. +pub struct Auctioneer { + shutdown_token: CancellationToken, + task: Option>>, } impl Auctioneer { @@ -66,11 +63,13 @@ impl Auctioneer { impl Future for Auctioneer { type Output = eyre::Result<()>; - fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { use futures::future::FutureExt as _; - let this = self.project(); - let task = this + let task = self .task .as_mut() .expect("the Auctioneer handle must not be polled after shutdown"); diff --git a/crates/astria-auctioneer/src/block/executed_stream.rs b/crates/astria-auctioneer/src/block/executed_stream.rs index ff93d0b71d..e68772a62d 100644 --- a/crates/astria-auctioneer/src/block/executed_stream.rs +++ b/crates/astria-auctioneer/src/block/executed_stream.rs @@ -16,7 +16,6 @@ use futures::{ Stream, StreamExt as _, }; -use pin_project_lite::pin_project; use telemetry::display::base64; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; @@ -50,12 +49,9 @@ impl Handle { } } -pin_project! { - /// A stream for receiving optimistic execution results from the rollup node. - pub(crate) struct ExecutedBlockStream { - #[pin] - client: tonic::Streaming, - } +/// A stream for receiving optimistic execution results from the rollup node. +pub(crate) struct ExecutedBlockStream { + client: tonic::Streaming, } impl ExecutedBlockStream { diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs index 67f15be3c3..83814d3bd2 100644 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ b/crates/astria-auctioneer/src/bundle/client.rs @@ -56,11 +56,8 @@ impl BundleClient { } } -pin_project_lite::pin_project! { - pub(crate) struct BundleStream { - #[pin] - inner: tonic::Streaming, - } +pub(crate) struct BundleStream { + inner: tonic::Streaming, } impl BundleStream { diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 65129b392c..ac4c648d44 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -79,12 +79,9 @@ impl SequencerChannel { } } -pin_project_lite::pin_project! { - /// A stream for receiving committed blocks from the sequencer. - pub(crate) struct BlockCommitmentStream { - #[pin] - inner: tonic::Streaming, - } +/// A stream for receiving committed blocks from the sequencer. +pub(crate) struct BlockCommitmentStream { + inner: tonic::Streaming, } impl BlockCommitmentStream { @@ -128,11 +125,8 @@ impl Stream for BlockCommitmentStream { } } -pin_project_lite::pin_project! { - pub(crate) struct OptimisticBlockStream { - #[pin] - inner: tonic::Streaming, - } +pub(crate) struct OptimisticBlockStream { + inner: tonic::Streaming, } impl OptimisticBlockStream { From df1eae6e6473db46af598957a17a46fc2d677985 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 22 Nov 2024 18:52:50 +0100 Subject: [PATCH 18/73] run rollup streams off a single channel also makes pushing into the duplex stream a method on on the stream (rather than on handle to it). moves conversion of optimistic to base block to outside the channel so that it doesn't get swallowed (the error event would be happening in a long-running span, if under any span at all). --- .../src/block/executed_stream.rs | 136 --------------- crates/astria-auctioneer/src/block/mod.rs | 8 +- crates/astria-auctioneer/src/bundle/client.rs | 108 ------------ crates/astria-auctioneer/src/bundle/mod.rs | 3 - crates/astria-auctioneer/src/lib.rs | 3 +- .../src/optimistic_executor/builder.rs | 6 +- .../src/optimistic_executor/mod.rs | 44 ++--- .../astria-auctioneer/src/rollup_channel.rs | 155 ++++++++++++++++++ 8 files changed, 191 insertions(+), 272 deletions(-) delete mode 100644 crates/astria-auctioneer/src/block/executed_stream.rs delete mode 100644 crates/astria-auctioneer/src/bundle/client.rs create mode 100644 crates/astria-auctioneer/src/rollup_channel.rs diff --git a/crates/astria-auctioneer/src/block/executed_stream.rs b/crates/astria-auctioneer/src/block/executed_stream.rs deleted file mode 100644 index e68772a62d..0000000000 --- a/crates/astria-auctioneer/src/block/executed_stream.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::pin::Pin; - -use astria_core::{ - generated::bundle::v1alpha1::{ - ExecuteOptimisticBlockStreamRequest, - ExecuteOptimisticBlockStreamResponse, - }, - primitive::v1::RollupId, - sequencerblock::v1::block::FilteredSequencerBlock, -}; -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use futures::{ - Stream, - StreamExt as _, -}; -use telemetry::display::base64; -use tokio::sync::mpsc; -use tokio_stream::wrappers::ReceiverStream; -use tracing::{ - debug, - error, -}; - -use super::{ - Executed, - Optimistic, -}; -use crate::optimistic_execution_client::OptimisticExecutionClient; - -pub(crate) struct Handle { - blocks_to_execute_tx: mpsc::Sender, -} - -impl Handle { - pub(crate) fn try_send_block_to_execute( - &mut self, - filtered_sequencer_block: FilteredSequencerBlock, - ) -> eyre::Result<()> { - self.blocks_to_execute_tx - .try_send(Optimistic { - filtered_sequencer_block, - }) - .wrap_err("failed to send block to execute")?; - - Ok(()) - } -} - -/// A stream for receiving optimistic execution results from the rollup node. -pub(crate) struct ExecutedBlockStream { - client: tonic::Streaming, -} - -impl ExecutedBlockStream { - pub(crate) async fn connect( - rollup_id: RollupId, - rollup_grpc_endpoint: String, - ) -> eyre::Result<(Handle, ExecutedBlockStream)> { - let mut optimistic_execution_client = OptimisticExecutionClient::new(&rollup_grpc_endpoint) - .wrap_err("failed to initialize optimistic execution client")?; - let (executed_stream_client, blocks_to_execute_tx) = optimistic_execution_client - .execute_optimistic_block_stream(rollup_id) - .await - .wrap_err("failed to stream executed blocks")?; - - Ok(( - Handle { - blocks_to_execute_tx, - }, - Self { - client: executed_stream_client, - }, - )) - } -} - -impl Stream for ExecutedBlockStream { - type Item = eyre::Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context, - ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.client.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); - }; - - let raw = res.wrap_err("received gRPC Error")?; - - let executed_block = - Executed::try_from_raw(raw).wrap_err("failed to parse raw to Executed")?; - - debug!( - executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), - executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), - "received block execution result" - ); - - std::task::Poll::Ready(Some(Ok(executed_block))) - } -} - -pub(crate) fn make_execution_requests_stream( - rollup_id: RollupId, -) -> ( - mpsc::Sender, - impl tonic::IntoStreamingRequest, -) { - // TODO: should this capacity be a config instead of a magic number? OPTIMISTIC_REORG_BUFFER? - // TODO: add a metric so we can see if that becomes a problem - let (blocks_to_execute_tx, blocks_to_execute_rx) = mpsc::channel(16); - let blocks_to_execute_stream_rx = ReceiverStream::new(blocks_to_execute_rx); - - let requests = blocks_to_execute_stream_rx.filter_map(move |block: Optimistic| async move { - let base_block = block - .try_into_base_block(rollup_id) - .wrap_err("failed to create BaseBlock from FilteredSequencerBlock"); - - // skip blocks which fail to decode the transactions? - match base_block { - Ok(base_block) => Some(ExecuteOptimisticBlockStreamRequest { - base_block: Some(base_block), - }), - Err(e) => { - error!(error = %e, "skipping execution of invalid block"); - - None - } - } - }); - - (blocks_to_execute_tx, requests) -} diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index f331ffa754..79fbd76120 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -31,8 +31,6 @@ use telemetry::display::base64; use crate::bundle::Bundle; -pub(crate) mod executed_stream; - /// Converts a [`tendermint::Time`] to a [`prost_types::Timestamp`]. fn convert_tendermint_time_to_protobuf_timestamp( value: sequencer_client::tendermint::Time, @@ -54,6 +52,12 @@ pub(crate) struct Optimistic { } impl Optimistic { + pub(crate) fn new(filtered_sequencer_block: FilteredSequencerBlock) -> Self { + Self { + filtered_sequencer_block, + } + } + /// Converts this [`Optimistic`] into a [`BaseBlock`] for the given `rollup_id`. /// If there are no transactions for the given `rollup_id`, this will return a `BaseBlock` /// with no transactions. diff --git a/crates/astria-auctioneer/src/bundle/client.rs b/crates/astria-auctioneer/src/bundle/client.rs deleted file mode 100644 index 83814d3bd2..0000000000 --- a/crates/astria-auctioneer/src/bundle/client.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::pin::Pin; - -use astria_core::generated::bundle::v1alpha1::{ - bundle_service_client::BundleServiceClient, - GetBundleStreamRequest, - GetBundleStreamResponse, -}; -use astria_eyre::eyre::{ - self, - eyre, - WrapErr as _, -}; -use futures::{ - Stream, - StreamExt as _, -}; -use prost::Name; -use tonic::transport::{ - Endpoint, - Uri, -}; -use tracing::instrument; - -use super::Bundle; - -pub(crate) struct BundleClient { - inner: BundleServiceClient, - uri: Uri, -} - -impl BundleClient { - pub(crate) fn new(rollup_uri: &str) -> eyre::Result { - let uri = rollup_uri - .parse::() - .wrap_err("failed to parse rollup_grpc_endpoint")?; - let endpoint = Endpoint::from(uri.clone()); - let client = BundleServiceClient::new(endpoint.connect_lazy()); - - Ok(Self { - inner: client, - uri, - }) - } - - #[instrument(skip_all, fields(uri = %self.uri))] - pub(crate) async fn get_bundle_stream( - &mut self, - ) -> eyre::Result> { - let mut client = self.inner.clone(); - let stream = client - .get_bundle_stream(GetBundleStreamRequest {}) - .await - .wrap_err("failed to open bundle stream")? - .into_inner(); - Ok(stream) - } -} - -pub(crate) struct BundleStream { - inner: tonic::Streaming, -} - -impl BundleStream { - pub(crate) async fn connect(rollup_grpc_endpoint: String) -> eyre::Result { - let mut client = BundleClient::new(&rollup_grpc_endpoint) - .wrap_err("failed to initialize bundle service client")?; - let stream = client - .get_bundle_stream() - .await - .wrap_err("failed to get bundle stream")?; - - Ok(Self { - inner: stream, - }) - } -} - -impl Stream for BundleStream { - type Item = eyre::Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.inner.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); - }; - - let raw = res - .wrap_err("failed receiving streamed message from server")? - .bundle - .ok_or_else(|| { - eyre!( - "message field not set: `{}.bundle`", - GetBundleStreamResponse::full_name() - ) - })?; - - let bundle = Bundle::try_from_raw(raw).wrap_err_with(|| { - format!( - "failed to validate received `{}`", - astria_core::generated::bundle::v1alpha1::Bundle::full_name() - ) - })?; - - std::task::Poll::Ready(Some(Ok(bundle))) - } -} diff --git a/crates/astria-auctioneer/src/bundle/mod.rs b/crates/astria-auctioneer/src/bundle/mod.rs index 946adcf841..b1b1c4f5bc 100644 --- a/crates/astria-auctioneer/src/bundle/mod.rs +++ b/crates/astria-auctioneer/src/bundle/mod.rs @@ -20,13 +20,10 @@ use astria_eyre::eyre::{ WrapErr as _, }; use bytes::Bytes; -pub(crate) use client::BundleStream; use prost::Message as _; use crate::sequencer_key::SequencerKey; -mod client; - // TODO: this should probably be moved to astria_core::bundle? #[derive(Debug, Clone)] pub(crate) struct Bundle { diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index fc1cd6a00a..f3f413ded5 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -7,8 +7,9 @@ mod build_info; mod bundle; pub mod config; pub(crate) mod metrics; -mod optimistic_execution_client; +// mod optimistic_execution_client; mod optimistic_executor; +mod rollup_channel; mod sequencer_channel; mod sequencer_key; diff --git a/crates/astria-auctioneer/src/optimistic_executor/builder.rs b/crates/astria-auctioneer/src/optimistic_executor/builder.rs index 46c515328c..0b4c1f421f 100644 --- a/crates/astria-auctioneer/src/optimistic_executor/builder.rs +++ b/crates/astria-auctioneer/src/optimistic_executor/builder.rs @@ -8,6 +8,7 @@ use tokio_util::sync::CancellationToken; use super::Startup; use crate::{ auction, + rollup_channel::RollupChannel, sequencer_channel::SequencerChannel, Metrics, }; @@ -39,6 +40,9 @@ impl Builder { } = self; let rollup_id = RollupId::from_unhashed_bytes(&rollup_id); + let rollup_channel = RollupChannel::create(&rollup_grpc_endpoint).wrap_err_with(|| { + format!("failed to create a gRPC channel to rollup at `{rollup_grpc_endpoint}`") + })?; let sequencer_channel = SequencerChannel::create(&sequencer_grpc_endpoint).wrap_err_with(|| { format!( @@ -50,7 +54,7 @@ impl Builder { metrics, shutdown_token, rollup_id, - rollup_grpc_endpoint, + rollup_channel, sequencer_channel, auctions, }) diff --git a/crates/astria-auctioneer/src/optimistic_executor/mod.rs b/crates/astria-auctioneer/src/optimistic_executor/mod.rs index 1efa2d788f..77dde92ac3 100644 --- a/crates/astria-auctioneer/src/optimistic_executor/mod.rs +++ b/crates/astria-auctioneer/src/optimistic_executor/mod.rs @@ -54,13 +54,12 @@ use tracing::{ use crate::{ auction, - block::{ - self, - executed_stream::ExecutedBlockStream, - }, - bundle::{ - Bundle, + block, + bundle::Bundle, + rollup_channel::{ BundleStream, + ExecuteOptimisticBlockStream, + RollupChannel, }, sequencer_channel::{ BlockCommitmentStream, @@ -87,7 +86,7 @@ pub(crate) struct Startup { shutdown_token: CancellationToken, sequencer_channel: SequencerChannel, rollup_id: RollupId, - rollup_grpc_endpoint: String, + rollup_channel: RollupChannel, auctions: auction::Manager, } @@ -98,30 +97,29 @@ impl Startup { shutdown_token, mut sequencer_channel, rollup_id, - rollup_grpc_endpoint, + rollup_channel, auctions, } = self; - let (execution_stream_handle, executed_blocks) = - ExecutedBlockStream::connect(rollup_id, rollup_grpc_endpoint.clone()) - .await - .wrap_err("failed to initialize executed block stream")?; + let executed_blocks = rollup_channel + .open_execute_optimistic_block_stream() + .await + .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; let mut optimistic_blocks = sequencer_channel .open_get_optimistic_block_stream(rollup_id) .await .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; - // TODO: create a way to forward the optimistic blocks to the execution stream. - let block_commitments = sequencer_channel .open_get_block_commitment_stream() .await .wrap_err("opening stream to receive block commitments from sequencer failed")?; - let bundle_stream = BundleStream::connect(rollup_grpc_endpoint) + let bundle_stream = rollup_channel + .open_bundle_stream() .await - .wrap_err("failed to initialize bundle stream")?; + .wrap_err("opening stream to receive bundles from rollup failed")?; let optimistic_block = optimistic_blocks .next() @@ -139,7 +137,7 @@ impl Startup { bundle_stream, auctions, current_block, - execution_stream_handle, + rollup_id, }) } } @@ -151,11 +149,11 @@ pub(crate) struct Running { shutdown_token: CancellationToken, optimistic_blocks: OptimisticBlockStream, block_commitments: BlockCommitmentStream, - executed_blocks: ExecutedBlockStream, + executed_blocks: ExecuteOptimisticBlockStream, bundle_stream: BundleStream, auctions: auction::Manager, current_block: block::Current, - execution_stream_handle: crate::block::executed_stream::Handle, + rollup_id: RollupId, } impl Running { @@ -231,8 +229,12 @@ impl Running { auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); self.auctions.new_auction(new_auction_id); - self.execution_stream_handle - .try_send_block_to_execute(optimistic_block) + let base_block = crate::block::Optimistic::new(optimistic_block) + .try_into_base_block(self.rollup_id) + // FIXME: give this their proper wire names + .wrap_err("failed to create BaseBlock from FilteredSequencerBlock")?; + self.executed_blocks + .try_send(base_block) .wrap_err("failed to forward block to execution stream")?; Ok(()) diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs new file mode 100644 index 0000000000..6bba8b9dd4 --- /dev/null +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -0,0 +1,155 @@ +use std::time::Duration; + +use astria_core::generated::bundle::v1alpha1::{ + bundle_service_client::BundleServiceClient, + BaseBlock, + ExecuteOptimisticBlockStreamResponse, + GetBundleStreamResponse, +}; +use astria_eyre::eyre::{ + self, + eyre, + WrapErr as _, +}; +use futures::{ + Stream, + StreamExt, +}; +use prost::Name as _; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; +use tonic::transport::Channel; + +use crate::bundle::Bundle; + +pub(crate) struct RollupChannel { + inner: Channel, +} + +impl RollupChannel { + pub(crate) fn create(uri: &str) -> eyre::Result { + let channel = Channel::from_shared(uri.to_string()) + .wrap_err("failed to open a channel to the provided uri")? + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(2)) + .connect_lazy(); + + Ok(Self { + inner: channel, + }) + } + + pub(crate) async fn open_bundle_stream(&self) -> eyre::Result { + use astria_core::generated::bundle::v1alpha1::GetBundleStreamRequest; + let inner = BundleServiceClient::new(self.inner.clone()) + .get_bundle_stream(GetBundleStreamRequest {}) + .await + .wrap_err("failed to open get bundle stream")? + .into_inner(); + Ok(BundleStream { + inner, + }) + } + + pub(crate) async fn open_execute_optimistic_block_stream( + &self, + ) -> eyre::Result { + use astria_core::generated::bundle::v1alpha1::{ + optimistic_execution_service_client::OptimisticExecutionServiceClient, + ExecuteOptimisticBlockStreamRequest, + }; + + let (to_server_tx, to_server_rx) = mpsc::channel(16); + let out_stream = ReceiverStream::new(to_server_rx).map(|base_block| { + ExecuteOptimisticBlockStreamRequest { + base_block: Some(base_block), + } + }); + let from_server = OptimisticExecutionServiceClient::new(self.inner.clone()) + .execute_optimistic_block_stream(out_stream) + .await + .wrap_err("failed to open execute optimistic block stream")? + .into_inner(); + + Ok(ExecuteOptimisticBlockStream { + incoming: from_server, + outgoing: to_server_tx, + }) + } +} + +pub(crate) struct BundleStream { + inner: tonic::Streaming, +} + +impl Stream for BundleStream { + type Item = eyre::Result; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { + return std::task::Poll::Ready(None); + }; + + let raw = res + .wrap_err("failed receiving streamed message from server")? + .bundle + .ok_or_else(|| { + eyre!( + "message field not set: `{}.bundle`", + GetBundleStreamResponse::full_name() + ) + })?; + + let bundle = Bundle::try_from_raw(raw).wrap_err_with(|| { + format!( + "failed to validate received `{}`", + astria_core::generated::bundle::v1alpha1::Bundle::full_name() + ) + })?; + + std::task::Poll::Ready(Some(Ok(bundle))) + } +} + +pub(crate) struct ExecuteOptimisticBlockStream { + incoming: tonic::Streaming, + outgoing: mpsc::Sender, +} + +impl ExecuteOptimisticBlockStream { + /// Immediately sends `base_block` to the connected server. Fails if + /// the channel is full. + // NOTE: just leak the tokio mpsc error for now. It's crate private anyway + // and we'd just end up wrapping the same variants. + pub(crate) fn try_send( + &mut self, + base_block: BaseBlock, + ) -> Result<(), tokio::sync::mpsc::error::TrySendError> { + self.outgoing.try_send(base_block) + } +} + +impl Stream for ExecuteOptimisticBlockStream { + type Item = eyre::Result; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let Some(message) = std::task::ready!(self.incoming.poll_next_unpin(cx)) else { + return std::task::Poll::Ready(None); + }; + + let message = message.wrap_err("failed receiving message over stream")?; + let executed_block = crate::block::Executed::try_from_raw(message).wrap_err_with(|| { + format!( + "failed to validate `{}`", + astria_core::generated::bundle::v1alpha1::ExecuteOptimisticBlockStreamResponse::full_name(), + ) + })?; + std::task::Poll::Ready(Some(Ok(executed_block))) + } +} From 1dd405f7b3d48464f864842816f0237b7722742d Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 14:47:34 +0100 Subject: [PATCH 19/73] flatten optimistic executioner into auctioneer The "optimistic executioner" task was an extra indirection that provided no clear benefit over just running all stream directly in the auctioneer task. --- .../astria-auctioneer/src/auctioneer/inner.rs | 373 ++++++++++++++---- .../astria-auctioneer/src/auctioneer/mod.rs | 2 +- crates/astria-auctioneer/src/lib.rs | 2 - .../src/optimistic_execution_client.rs | 67 ---- .../src/optimistic_executor/builder.rs | 62 --- .../src/optimistic_executor/mod.rs | 317 --------------- .../astria-auctioneer/src/rollup_channel.rs | 7 +- .../src/sequencer_channel.rs | 7 +- 8 files changed, 305 insertions(+), 532 deletions(-) delete mode 100644 crates/astria-auctioneer/src/optimistic_execution_client.rs delete mode 100644 crates/astria-auctioneer/src/optimistic_executor/builder.rs delete mode 100644 crates/astria-auctioneer/src/optimistic_executor/mod.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index 4ee1e460e9..f38f432335 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -1,44 +1,57 @@ use std::time::Duration; +use astria_core::{ + primitive::v1::RollupId, + sequencerblock::v1::block::FilteredSequencerBlock, +}; use astria_eyre::eyre::{ self, + OptionExt as _, WrapErr as _, }; -use itertools::Itertools as _; -use tokio::{ - select, - time::timeout, -}; -use tokio_util::{ - sync::CancellationToken, - task::JoinMap, -}; +use futures::StreamExt as _; +use telemetry::display::base64; +use tokio::select; +use tokio_util::sync::CancellationToken; use tracing::{ error, info, + instrument, warn, }; use crate::{ auction, - flatten_join_result, - optimistic_executor, + rollup_channel::{ + BundleStream, + ExecuteOptimisticBlockStream, + RollupChannel, + }, + sequencer_channel::{ + BlockCommitmentStream, + OptimisticBlockStream, + SequencerChannel, + }, Config, Metrics, }; +macro_rules! break_for_closed_stream { + ($stream_res:expr, $msg:expr) => { + match $stream_res { + Some(val) => val, + None => break Err(::astria_eyre::eyre::eyre!($msg)), + } + }; +} + /// The implementation of the auctioneer business logic. pub(super) struct Inner { /// Used to signal the service to shutdown - shutdown_token: CancellationToken, - - /// The different long-running tasks that make up the Auctioneer - tasks: JoinMap<&'static str, eyre::Result<()>>, + run_state: RunState, } impl Inner { - const OPTIMISTIC_EXECUTOR: &'static str = "optimistic_executor"; - /// Creates an [`Auctioneer`] service from a [`Config`] and [`Metrics`]. pub(super) fn new( cfg: Config, @@ -58,8 +71,10 @@ impl Inner { .. } = cfg; - let mut tasks = JoinMap::new(); + let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; + let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; + // TODO: Rearchitect this thing let auctions = auction::manager::Builder { metrics, shutdown_token: shutdown_token.clone(), @@ -75,89 +90,285 @@ impl Inner { .build() .wrap_err("failed to initialize auction manager")?; - let optimistic_executor = optimistic_executor::Builder { - metrics, - shutdown_token: shutdown_token.clone(), - sequencer_grpc_endpoint, - rollup_id, - rollup_grpc_endpoint, - auctions, - } - .build() - .wrap_err("failed to construct optimistic executor")?; - - tasks.spawn(Self::OPTIMISTIC_EXECUTOR, async { - optimistic_executor - .startup() - .await - .wrap_err("optimistic executor startup failed")? - .run() - .await - }); - Ok(Self { - shutdown_token, - tasks, + run_state: Starting { + auctions, + rollup_channel, + rollup_id: RollupId::from_unhashed_bytes(&rollup_id), + sequencer_channel, + shutdown_token, + } + .into(), }) } /// Runs the [`Auctioneer`] service until it received an exit signal, or one of the constituent /// tasks either ends unexpectedly or returns an error. - pub(super) async fn run(mut self) -> eyre::Result<()> { - let reason = select! { - biased; - - () = self.shutdown_token.cancelled() => { - Ok("auctioneer received shutdown signal") - }, - - Some((name, res)) = self.tasks.join_next() => { - flatten_join_result(res) - .wrap_err_with(|| format!("task `{name}` failed")) - .map(|()| "task `{name}` exited unexpectedly") - } - }; + pub(super) async fn run(self) -> eyre::Result<()> { + let Self { + mut run_state, + } = self; - match reason { - Ok(msg) => info!(%msg, "received shutdown signal"), - Err(err) => error!(%err, "shutting down due to error"), + loop { + match run_state { + RunState::Cancelled => break Ok(()), + RunState::Starting(starting) => match starting.run().await { + Ok(new_state) => run_state = new_state, + Err(err) => break Err(err).wrap_err("failed during startup"), + }, + RunState::Running(running) => match running.run().await { + Ok(new_state) => run_state = new_state, + Err(err) => break Err(err).wrap_err("failed during execution"), + }, + } } + } +} - self.shutdown().await; - Ok(()) +enum RunState { + Cancelled, + Starting(Starting), + Running(Running), +} + +impl From for RunState { + fn from(value: Running) -> Self { + Self::Running(value) + } +} + +impl From for RunState { + fn from(value: Starting) -> Self { + Self::Starting(value) + } +} + +struct Starting { + auctions: auction::Manager, + rollup_channel: RollupChannel, + rollup_id: RollupId, + sequencer_channel: SequencerChannel, + shutdown_token: CancellationToken, +} + +impl Starting { + async fn run(self) -> eyre::Result { + let Self { + auctions, + rollup_id, + rollup_channel, + mut sequencer_channel, + shutdown_token, + } = self; + + let executed_blocks = rollup_channel + .open_execute_optimistic_block_stream() + .await + .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; + + let mut optimistic_blocks = sequencer_channel + .open_get_optimistic_block_stream(rollup_id) + .await + .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; + + let block_commitments = sequencer_channel + .open_get_block_commitment_stream() + .await + .wrap_err("opening stream to receive block commitments from sequencer failed")?; + + let bundles = rollup_channel + .open_bundle_stream() + .await + .wrap_err("opening stream to receive bundles from rollup failed")?; + + let optimistic_block = optimistic_blocks + .next() + .await + .ok_or_eyre("optimistic stream closed during startup?")? + .wrap_err("failed to get optimistic block during startup")?; + let current_block = crate::block::Current::with_optimistic(optimistic_block); + + Ok(Running { + auctions, + block_commitments, + bundles, + current_block, + executed_blocks, + optimistic_blocks, + rollup_id, + shutdown_token, + } + .into()) } +} + +struct Running { + auctions: auction::Manager, + block_commitments: BlockCommitmentStream, + bundles: BundleStream, + current_block: crate::block::Current, + executed_blocks: ExecuteOptimisticBlockStream, + optimistic_blocks: OptimisticBlockStream, + rollup_id: RollupId, + shutdown_token: CancellationToken, +} + +impl Running { + async fn run(mut self) -> eyre::Result { + let reason: eyre::Result<&str> = { + // This is a long running loop. Errors are emitted inside the handlers. + loop { + select! { + biased; + () = self.shutdown_token.cancelled() => { + break Ok("received shutdown signal"); + }, + + Some((id, res)) = self.auctions.join_next() => { + res.wrap_err_with(|| format!("auction failed for block {}", base64(id)))?; + }, + + res = self.optimistic_blocks.next() => { + let res = break_for_closed_stream!(res, "optimistic block stream closed"); + let _ = self.handle_optimistic_block(res); + }, + + res = self.block_commitments.next() => { + let res = break_for_closed_stream!(res, "block commitment stream closed"); + + let _ = self.handle_block_commitment(res); + + }, - /// Initiates shutdown of the Auctioneer and waits for all the constituent tasks to shut down. - async fn shutdown(mut self) { - self.shutdown_token.cancel(); + res = self.executed_blocks.next() => { + let res = break_for_closed_stream!(res, "executed block stream closed"); - let shutdown_loop = async { - while let Some((name, res)) = self.tasks.join_next().await { - let message = "task shut down"; - match flatten_join_result(res) { - Ok(()) => { - info!(name, message); + let _ = self.handle_executed_block(res); } - Err(err) => { - error!(name, %err, message); + + Some(res) = self.bundles.next() => { + let bundle = res.wrap_err("failed to get bundle")?; + + let _ = self.handle_bundle(bundle); } } } }; - info!("signalling all tasks to shut down; waiting 25 seconds for exit"); - if timeout(Duration::from_secs(25), shutdown_loop) - .await - .is_err() - { - let tasks = self.tasks.keys().join(", "); + match reason { + Ok(msg) => info!(%msg, "shutting down"), + Err(err) => error!(%err, "shutting down due to error"), + }; + + Ok(RunState::Cancelled) + } + + #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] + fn handle_optimistic_block( + &mut self, + optimistic_block: eyre::Result, + ) -> eyre::Result<()> { + let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; + + let old_auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions + .abort_auction(old_auction_id) + .wrap_err("failed to abort auction")?; + + info!( + optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), + "received optimistic block, aborting old auction and starting new auction" + ); + + self.current_block = crate::block::Current::with_optimistic(optimistic_block.clone()); + let new_auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions.new_auction(new_auction_id); + + let base_block = crate::block::Optimistic::new(optimistic_block) + .try_into_base_block(self.rollup_id) + // FIXME: give this their proper wire names + .wrap_err("failed to create BaseBlock from FilteredSequencerBlock")?; + self.executed_blocks + .try_send(base_block) + .wrap_err("failed to forward block to execution stream")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())), err)] + fn handle_block_commitment( + &mut self, + block_commitment: eyre::Result, + ) -> eyre::Result<()> { + let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; + + if let Err(e) = self.current_block.commitment(block_commitment.clone()) { warn!( - tasks = format_args!("[{tasks}]"), - "aborting all tasks that have not yet shut down" + current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + block_commitment.sequencer_block_hash = %base64(block_commitment.sequencer_block_hash()), + "received block commitment for the wrong block" ); - } else { - info!("all tasks have shut down regularly"); + return Err(e).wrap_err("failed to handle block commitment"); } - info!("shutting down"); + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + + self.auctions + .start_timer(auction_id) + .wrap_err("failed to start timer")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + fn handle_executed_block( + &mut self, + executed_block: eyre::Result, + ) -> eyre::Result<()> { + let executed_block = executed_block.wrap_err("failed to receive executed block")?; + + if let Err(e) = self.current_block.execute(executed_block.clone()) { + warn!( + // TODO: nicer display for the current block + current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), + executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), + "received optimistic execution result for wrong sequencer block" + ); + return Err(e).wrap_err("failed to handle executed block"); + } + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + + self.auctions + .start_processing_bids(auction_id) + .wrap_err("failed to start processing bids")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + fn handle_bundle(&mut self, bundle: crate::bundle::Bundle) -> eyre::Result<()> { + if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { + warn!( + curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), + bundle.parent_rollup_block_hash = %base64(bundle.parent_rollup_block_hash()), + "incoming bundle does not match current block, ignoring" + ); + return Err(e).wrap_err("failed to handle bundle"); + } + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions + .try_send_bundle(auction_id, bundle) + .wrap_err("failed to submit bundle to auction")?; + + Ok(()) } } diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index a868090e43..c803a205bf 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -72,7 +72,7 @@ impl Future for Auctioneer { let task = self .task .as_mut() - .expect("the Auctioneer handle must not be polled after shutdown"); + .expect("auctioneer must not be polled after shutdown"); task.poll_unpin(cx).map(flatten_join_result) } } diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index f3f413ded5..b57a161f52 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -7,8 +7,6 @@ mod build_info; mod bundle; pub mod config; pub(crate) mod metrics; -// mod optimistic_execution_client; -mod optimistic_executor; mod rollup_channel; mod sequencer_channel; mod sequencer_key; diff --git a/crates/astria-auctioneer/src/optimistic_execution_client.rs b/crates/astria-auctioneer/src/optimistic_execution_client.rs deleted file mode 100644 index cac04be784..0000000000 --- a/crates/astria-auctioneer/src/optimistic_execution_client.rs +++ /dev/null @@ -1,67 +0,0 @@ -use astria_core::{ - generated::bundle::v1alpha1::{ - optimistic_execution_service_client::OptimisticExecutionServiceClient, - ExecuteOptimisticBlockStreamResponse, - }, - primitive::v1::RollupId, -}; -use astria_eyre::eyre::{ - self, - Context, -}; -use tokio::sync::mpsc; -use tonic::transport::{ - Channel, - Endpoint, - Uri, -}; -use tracing::instrument; - -use crate::block::{ - self, - executed_stream::make_execution_requests_stream, -}; - -pub(crate) struct OptimisticExecutionClient { - inner: OptimisticExecutionServiceClient, - uri: Uri, -} - -impl OptimisticExecutionClient { - pub(crate) fn new(rollup_uri: &str) -> eyre::Result { - let uri = rollup_uri - .parse::() - .wrap_err("failed parsing optimistic execution uri")?; - - // TODO: use UDS socket - let endpoint = Endpoint::from(uri.clone()); - let inner = OptimisticExecutionServiceClient::new(endpoint.connect_lazy()); - - Ok(Self { - inner, - uri, - }) - } - - #[instrument(skip_all, fields( - uri = %self.uri, - %rollup_id, - err, - ))] - pub(crate) async fn execute_optimistic_block_stream( - &mut self, - rollup_id: RollupId, - ) -> eyre::Result<( - tonic::Streaming, - mpsc::Sender, - )> { - let (blocks_to_execute_tx, requests) = make_execution_requests_stream(rollup_id); - let stream = self - .inner - .execute_optimistic_block_stream(requests) - .await - .wrap_err("failed to open execute optimistic block stream")? - .into_inner(); - Ok((stream, blocks_to_execute_tx)) - } -} diff --git a/crates/astria-auctioneer/src/optimistic_executor/builder.rs b/crates/astria-auctioneer/src/optimistic_executor/builder.rs deleted file mode 100644 index 0b4c1f421f..0000000000 --- a/crates/astria-auctioneer/src/optimistic_executor/builder.rs +++ /dev/null @@ -1,62 +0,0 @@ -use astria_core::primitive::v1::RollupId; -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use tokio_util::sync::CancellationToken; - -use super::Startup; -use crate::{ - auction, - rollup_channel::RollupChannel, - sequencer_channel::SequencerChannel, - Metrics, -}; - -pub(crate) struct Builder { - pub(crate) metrics: &'static Metrics, - pub(crate) shutdown_token: CancellationToken, - /// The endpoint for the sequencer gRPC service used for the optimistic block stream - pub(crate) sequencer_grpc_endpoint: String, - /// The file path for the private key used to sign sequencer transactions with the auction - /// results - /// The rollup ID for the filtered optimistic block stream - pub(crate) rollup_id: String, - /// The endpoint for the rollup's optimistic execution gRPC service - pub(crate) rollup_grpc_endpoint: String, - /// Manager for ongoing auctions - pub(crate) auctions: auction::Manager, -} - -impl Builder { - pub(crate) fn build(self) -> eyre::Result { - let Self { - metrics, - shutdown_token, - sequencer_grpc_endpoint, - rollup_id, - rollup_grpc_endpoint, - auctions, - } = self; - - let rollup_id = RollupId::from_unhashed_bytes(&rollup_id); - let rollup_channel = RollupChannel::create(&rollup_grpc_endpoint).wrap_err_with(|| { - format!("failed to create a gRPC channel to rollup at `{rollup_grpc_endpoint}`") - })?; - let sequencer_channel = - SequencerChannel::create(&sequencer_grpc_endpoint).wrap_err_with(|| { - format!( - "failed to create a gRPC channel to Sequencer at `{sequencer_grpc_endpoint}`" - ) - })?; - - Ok(Startup { - metrics, - shutdown_token, - rollup_id, - rollup_channel, - sequencer_channel, - auctions, - }) - } -} diff --git a/crates/astria-auctioneer/src/optimistic_executor/mod.rs b/crates/astria-auctioneer/src/optimistic_executor/mod.rs deleted file mode 100644 index 77dde92ac3..0000000000 --- a/crates/astria-auctioneer/src/optimistic_executor/mod.rs +++ /dev/null @@ -1,317 +0,0 @@ -//! The Optimistic Executor is the component responsible for maintaining the current block -//! state based on the optimistic block stream, the block commitment stream, and the executed -//! block stream. The Optimistic Executior uses its current block state for running an auction -//! per block. Incoming bundles are fed to the current auction after checking them against the -//! current block state. -//! -//! ## Block Lifecycle -//! The Optimistic Executor tracks its current block using the `block:Current` struct, at a high -//! level: -//! 1. Blocks are received optimistically from the sequencer via the optimistic block stream, which -//! also forwards them to the rollup node for execution. -//! 2. Execution results are received from the rollup node via the executed block stream. -//! 3. Commitments are received from the sequencer via the block commitment stream. -//! -//! ## Auction Lifecycle -//! The current block state is used for running an auction per block. Auctions are managed by the -//! `auction::Manager` struct, and the Optimistic Executor advances their state based on its current -//! block state: -//! 1. An auction is created when a new block is received optimistically. -//! 2. The auction will begin processing bids when the executed block is received. -//! 3. The auction's timer is started when a block commitment is received. -//! 4. Bundles are fed to the current auction after checking them against the current block state. -//! -//! ### Bundles and Backpressure -//! Bundles are fed to the current auction via an mpsc channel. Since the auction will only start -//! processing bids after the executed block is received, the channel is used to buffer bundles -//! that are received before the executed block. -//! If too many bundles are received from the rollup node before the block is executed -//! optimistically on the rollup node, the channel will fill up and newly received bundles will be -//! dropped until the auction begins processing bundles. -//! We assume this is highly unlikely, as the rollup node's should filter the bundles it streams -//! by its optimistic head block hash. - -use astria_core::{ - primitive::v1::RollupId, - sequencerblock::v1::block::FilteredSequencerBlock, -}; -use astria_eyre::eyre::{ - self, - eyre, - OptionExt, - WrapErr as _, -}; -use futures::StreamExt as _; -use telemetry::display::base64; -use tokio::select; -use tokio_util::sync::CancellationToken; -use tracing::{ - error, - info, - instrument, - warn, -}; - -use crate::{ - auction, - block, - bundle::Bundle, - rollup_channel::{ - BundleStream, - ExecuteOptimisticBlockStream, - RollupChannel, - }, - sequencer_channel::{ - BlockCommitmentStream, - OptimisticBlockStream, - SequencerChannel, - }, -}; - -mod builder; -pub(crate) use builder::Builder; - -macro_rules! break_for_closed_stream { - ($stream_res:expr, $msg:expr) => { - match $stream_res { - Some(val) => val, - None => break Err(eyre!($msg)), - } - }; -} - -pub(crate) struct Startup { - #[allow(dead_code)] - metrics: &'static crate::Metrics, - shutdown_token: CancellationToken, - sequencer_channel: SequencerChannel, - rollup_id: RollupId, - rollup_channel: RollupChannel, - auctions: auction::Manager, -} - -impl Startup { - pub(crate) async fn startup(self) -> eyre::Result { - let Self { - metrics, - shutdown_token, - mut sequencer_channel, - rollup_id, - rollup_channel, - auctions, - } = self; - - let executed_blocks = rollup_channel - .open_execute_optimistic_block_stream() - .await - .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; - - let mut optimistic_blocks = sequencer_channel - .open_get_optimistic_block_stream(rollup_id) - .await - .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; - - let block_commitments = sequencer_channel - .open_get_block_commitment_stream() - .await - .wrap_err("opening stream to receive block commitments from sequencer failed")?; - - let bundle_stream = rollup_channel - .open_bundle_stream() - .await - .wrap_err("opening stream to receive bundles from rollup failed")?; - - let optimistic_block = optimistic_blocks - .next() - .await - .ok_or_eyre("optimistic stream closed during startup?")? - .wrap_err("failed to get optimistic block during startup")?; - let current_block = block::Current::with_optimistic(optimistic_block); - - Ok(Running { - metrics, - shutdown_token, - optimistic_blocks, - block_commitments, - executed_blocks, - bundle_stream, - auctions, - current_block, - rollup_id, - }) - } -} - -pub(crate) struct Running { - // TODO: add metrics - #[allow(dead_code)] - metrics: &'static crate::Metrics, - shutdown_token: CancellationToken, - optimistic_blocks: OptimisticBlockStream, - block_commitments: BlockCommitmentStream, - executed_blocks: ExecuteOptimisticBlockStream, - bundle_stream: BundleStream, - auctions: auction::Manager, - current_block: block::Current, - rollup_id: RollupId, -} - -impl Running { - pub(crate) async fn run(mut self) -> eyre::Result<()> { - let reason: eyre::Result<&str> = { - // This is a long running loop. Errors are emitted inside the handlers. - loop { - select! { - biased; - () = self.shutdown_token.cancelled() => { - break Ok("received shutdown signal"); - }, - - Some((id, res)) = self.auctions.join_next() => { - res.wrap_err_with(|| format!("auction failed for block {}", base64(id)))?; - }, - - res = self.optimistic_blocks.next() => { - let res = break_for_closed_stream!(res, "optimistic block stream closed"); - let _ = self.handle_optimistic_block(res); - }, - - res = self.block_commitments.next() => { - let res = break_for_closed_stream!(res, "block commitment stream closed"); - - let _ = self.handle_block_commitment(res); - - }, - - res = self.executed_blocks.next() => { - let res = break_for_closed_stream!(res, "executed block stream closed"); - - let _ = self.handle_executed_block(res); - } - - Some(res) = self.bundle_stream.next() => { - let bundle = res.wrap_err("failed to get bundle")?; - - let _ = self.handle_bundle(bundle); - } - } - } - }; - - match reason { - Ok(msg) => info!(%msg, "shutting down"), - Err(err) => error!(%err, "shutting down due to error"), - }; - - Ok(()) - } - - #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] - fn handle_optimistic_block( - &mut self, - optimistic_block: eyre::Result, - ) -> eyre::Result<()> { - let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; - - let old_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .abort_auction(old_auction_id) - .wrap_err("failed to abort auction")?; - - info!( - optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), - "received optimistic block, aborting old auction and starting new auction" - ); - - self.current_block = block::Current::with_optimistic(optimistic_block.clone()); - let new_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions.new_auction(new_auction_id); - - let base_block = crate::block::Optimistic::new(optimistic_block) - .try_into_base_block(self.rollup_id) - // FIXME: give this their proper wire names - .wrap_err("failed to create BaseBlock from FilteredSequencerBlock")?; - self.executed_blocks - .try_send(base_block) - .wrap_err("failed to forward block to execution stream")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())), err)] - fn handle_block_commitment( - &mut self, - block_commitment: eyre::Result, - ) -> eyre::Result<()> { - let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; - - if let Err(e) = self.current_block.commitment(block_commitment.clone()) { - warn!( - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - block_commitment.sequencer_block_hash = %base64(block_commitment.sequencer_block_hash()), - "received block commitment for the wrong block" - ); - return Err(e).wrap_err("failed to handle block commitment"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - - self.auctions - .start_timer(auction_id) - .wrap_err("failed to start timer")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] - fn handle_executed_block( - &mut self, - executed_block: eyre::Result, - ) -> eyre::Result<()> { - let executed_block = executed_block.wrap_err("failed to receive executed block")?; - - if let Err(e) = self.current_block.execute(executed_block.clone()) { - warn!( - // TODO: nicer display for the current block - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), - executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), - "received optimistic execution result for wrong sequencer block" - ); - return Err(e).wrap_err("failed to handle executed block"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - - self.auctions - .start_processing_bids(auction_id) - .wrap_err("failed to start processing bids")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] - fn handle_bundle(&mut self, bundle: Bundle) -> eyre::Result<()> { - if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { - warn!( - curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - bundle.parent_rollup_block_hash = %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - return Err(e).wrap_err("failed to handle bundle"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .try_send_bundle(auction_id, bundle) - .wrap_err("failed to submit bundle to auction")?; - - Ok(()) - } -} diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 6bba8b9dd4..f37e1fa88e 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -22,12 +22,17 @@ use tonic::transport::Channel; use crate::bundle::Bundle; +pub(crate) fn open(endpoint: &str) -> eyre::Result { + RollupChannel::create(&endpoint) + .wrap_err_with(|| format!("failed to create a gRPC channel to rollup at `{endpoint}`")) +} + pub(crate) struct RollupChannel { inner: Channel, } impl RollupChannel { - pub(crate) fn create(uri: &str) -> eyre::Result { + fn create(uri: &str) -> eyre::Result { let channel = Channel::from_shared(uri.to_string()) .wrap_err("failed to open a channel to the provided uri")? .connect_timeout(Duration::from_secs(5)) diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index ac4c648d44..2217f2587e 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -28,12 +28,17 @@ use tonic::transport::Channel; use crate::block::Commitment; +pub(crate) fn open(endpoint: &str) -> eyre::Result { + SequencerChannel::create(&endpoint) + .wrap_err_with(|| format!("failed to create a gRPC channel to Sequencer at `{endpoint}`")) +} + pub(crate) struct SequencerChannel { inner: Channel, } impl SequencerChannel { - pub(crate) fn create(uri: &str) -> eyre::Result { + fn create(uri: &str) -> eyre::Result { let channel = Channel::from_shared(uri.to_string()) .wrap_err("failed to open a channel to the provided uri")? .connect_timeout(Duration::from_secs(5)) From 9ac15968a9cf0f28f342339c3c97013edd139839 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 15:38:52 +0100 Subject: [PATCH 20/73] have auctioneer shutdown events outside it's long running task --- .../astria-auctioneer/src/auctioneer/inner.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index f38f432335..bf541bb9c6 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -255,12 +255,7 @@ impl Running { } }; - match reason { - Ok(msg) => info!(%msg, "shutting down"), - Err(err) => error!(%err, "shutting down due to error"), - }; - - Ok(RunState::Cancelled) + self.shutdown(reason) } #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] @@ -371,4 +366,14 @@ impl Running { Ok(()) } + + #[instrument(skip_all)] + fn shutdown(self, reason: eyre::Result<&'static str>) -> eyre::Result { + let message: &str = "shutting down"; + match reason { + Ok(reason) => info!(%reason, message), + Err(reason) => error!(%reason, message), + }; + Ok(RunState::Cancelled) + } } From 2ef11e0c1c43ddcb227d7a72db6c7bc6be4dedf8 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 15:52:27 +0100 Subject: [PATCH 21/73] don't short circuit in the auctioneer select loop --- .../astria-auctioneer/src/auctioneer/inner.rs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index bf541bb9c6..44f7826f03 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -6,7 +6,7 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, - OptionExt as _, + OptionExt, WrapErr as _, }; use futures::StreamExt as _; @@ -36,11 +36,12 @@ use crate::{ Metrics, }; -macro_rules! break_for_closed_stream { - ($stream_res:expr, $msg:expr) => { - match $stream_res { - Some(val) => val, - None => break Err(::astria_eyre::eyre::eyre!($msg)), +/// To break from a loop instead of using the `?` operator. +macro_rules! try_break { + ($res:expr) => { + match $res { + Ok(val) => val, + Err(err) => break Err(err), } }; } @@ -225,30 +226,26 @@ impl Running { }, Some((id, res)) = self.auctions.join_next() => { - res.wrap_err_with(|| format!("auction failed for block {}", base64(id)))?; + try_break!(res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))); }, res = self.optimistic_blocks.next() => { - let res = break_for_closed_stream!(res, "optimistic block stream closed"); + let res = try_break!(res.ok_or_eyre("optimistic block stream closed")); let _ = self.handle_optimistic_block(res); }, res = self.block_commitments.next() => { - let res = break_for_closed_stream!(res, "block commitment stream closed"); - + let res = try_break!(res.ok_or_eyre("block commitment stream closed")); let _ = self.handle_block_commitment(res); - }, res = self.executed_blocks.next() => { - let res = break_for_closed_stream!(res, "executed block stream closed"); - + let res = try_break!(res.ok_or_eyre("executed block stream closed")); let _ = self.handle_executed_block(res); } Some(res) = self.bundles.next() => { let bundle = res.wrap_err("failed to get bundle")?; - let _ = self.handle_bundle(bundle); } } From 5a6945101d7dff75102d22c840f7604a3706073d Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 16:34:50 +0100 Subject: [PATCH 22/73] shutdown all still active auctions --- .../astria-auctioneer/src/auction/manager.rs | 6 +++++ .../astria-auctioneer/src/auctioneer/inner.rs | 26 ++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index c587196711..d422caa5a8 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -199,4 +199,10 @@ impl Manager { None } } + + pub(crate) fn abort_all(&mut self) -> usize { + let number_of_live_auctions = self.running_auctions.len(); + self.running_auctions.abort_all(); + number_of_live_auctions + } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index 44f7826f03..16f50565b1 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -252,7 +252,7 @@ impl Running { } }; - self.shutdown(reason) + self.shutdown(reason).await } #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] @@ -365,12 +365,32 @@ impl Running { } #[instrument(skip_all)] - fn shutdown(self, reason: eyre::Result<&'static str>) -> eyre::Result { - let message: &str = "shutting down"; + async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result { + const WAIT_BEFORE_ABORT: Duration = Duration::from_secs(25); + + let message = format!( + "waiting {} for all constituent tasks to shutdown before aborting", + humantime::format_duration(WAIT_BEFORE_ABORT), + ); match reason { Ok(reason) => info!(%reason, message), Err(reason) => error!(%reason, message), }; + let shutdown_auctions = async { + while let Some((id, res)) = self.auctions.join_next().await { + if let Err(error) = res { + // FIXME: probide a display impl for this ID + warn!(?id, %error, "auction ended with an error"); + } + } + }; + // NOTE: we don't care if this elapses. We will abort all auctions anyway + // and report if there were any still running. + let _ = tokio::time::timeout(WAIT_BEFORE_ABORT, shutdown_auctions).await; + let aborted = self.auctions.abort_all(); + if aborted > 0 { + warn!("aborted `{aborted}` auctions still running after grace period",); + } Ok(RunState::Cancelled) } } From dcc40dd9c66db73395bfec7995d5c741ede1603f Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 16:36:24 +0100 Subject: [PATCH 23/73] remember to shutdown all constituent tasks during shutdown --- crates/astria-auctioneer/src/auctioneer/inner.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index 16f50565b1..8016501ade 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -368,6 +368,10 @@ impl Running { async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result { const WAIT_BEFORE_ABORT: Duration = Duration::from_secs(25); + // Necessary if we got here because of another reason than receiving an external + // shutdown signal. + self.shutdown_token.cancel(); + let message = format!( "waiting {} for all constituent tasks to shutdown before aborting", humantime::format_duration(WAIT_BEFORE_ABORT), From bd7ceef4ec30a2dfa65a4bb0449d5c77b349633b Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sun, 24 Nov 2024 16:54:38 +0100 Subject: [PATCH 24/73] move running and starting runstates into their own modules --- .../astria-auctioneer/src/auctioneer/inner.rs | 342 +----------------- .../astria-auctioneer/src/auctioneer/mod.rs | 2 + .../src/auctioneer/running.rs | 241 ++++++++++++ .../src/auctioneer/starting.rs | 134 +++++++ 4 files changed, 385 insertions(+), 334 deletions(-) create mode 100644 crates/astria-auctioneer/src/auctioneer/running.rs create mode 100644 crates/astria-auctioneer/src/auctioneer/starting.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs index 8016501ade..19519d9d52 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -1,54 +1,20 @@ -use std::time::Duration; - -use astria_core::{ - primitive::v1::RollupId, - sequencerblock::v1::block::FilteredSequencerBlock, -}; use astria_eyre::eyre::{ self, - OptionExt, WrapErr as _, }; -use futures::StreamExt as _; -use telemetry::display::base64; -use tokio::select; use tokio_util::sync::CancellationToken; -use tracing::{ - error, - info, - instrument, - warn, -}; +use super::{ + running::Running, + starting::Starting, +}; use crate::{ - auction, - rollup_channel::{ - BundleStream, - ExecuteOptimisticBlockStream, - RollupChannel, - }, - sequencer_channel::{ - BlockCommitmentStream, - OptimisticBlockStream, - SequencerChannel, - }, Config, Metrics, }; -/// To break from a loop instead of using the `?` operator. -macro_rules! try_break { - ($res:expr) => { - match $res { - Ok(val) => val, - Err(err) => break Err(err), - } - }; -} - /// The implementation of the auctioneer business logic. pub(super) struct Inner { - /// Used to signal the service to shutdown run_state: RunState, } @@ -59,47 +25,10 @@ impl Inner { metrics: &'static Metrics, shutdown_token: CancellationToken, ) -> eyre::Result { - let Config { - sequencer_grpc_endpoint, - sequencer_abci_endpoint, - latency_margin_ms, - rollup_grpc_endpoint, - rollup_id, - sequencer_chain_id, - sequencer_private_key_path, - sequencer_address_prefix, - fee_asset_denomination, - .. - } = cfg; - - let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; - let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; - - // TODO: Rearchitect this thing - let auctions = auction::manager::Builder { - metrics, - shutdown_token: shutdown_token.clone(), - sequencer_grpc_endpoint: sequencer_grpc_endpoint.clone(), - sequencer_abci_endpoint, - latency_margin: Duration::from_millis(latency_margin_ms), - sequencer_private_key_path, - sequencer_address_prefix, - fee_asset_denomination, - sequencer_chain_id, - rollup_id: rollup_id.clone(), - } - .build() - .wrap_err("failed to initialize auction manager")?; - + let run_state = super::starting::run_state(cfg, shutdown_token, metrics) + .wrap_err("failed initializating in starting state")?; Ok(Self { - run_state: Starting { - auctions, - rollup_channel, - rollup_id: RollupId::from_unhashed_bytes(&rollup_id), - sequencer_channel, - shutdown_token, - } - .into(), + run_state, }) } @@ -126,7 +55,7 @@ impl Inner { } } -enum RunState { +pub(super) enum RunState { Cancelled, Starting(Starting), Running(Running), @@ -143,258 +72,3 @@ impl From for RunState { Self::Starting(value) } } - -struct Starting { - auctions: auction::Manager, - rollup_channel: RollupChannel, - rollup_id: RollupId, - sequencer_channel: SequencerChannel, - shutdown_token: CancellationToken, -} - -impl Starting { - async fn run(self) -> eyre::Result { - let Self { - auctions, - rollup_id, - rollup_channel, - mut sequencer_channel, - shutdown_token, - } = self; - - let executed_blocks = rollup_channel - .open_execute_optimistic_block_stream() - .await - .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; - - let mut optimistic_blocks = sequencer_channel - .open_get_optimistic_block_stream(rollup_id) - .await - .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; - - let block_commitments = sequencer_channel - .open_get_block_commitment_stream() - .await - .wrap_err("opening stream to receive block commitments from sequencer failed")?; - - let bundles = rollup_channel - .open_bundle_stream() - .await - .wrap_err("opening stream to receive bundles from rollup failed")?; - - let optimistic_block = optimistic_blocks - .next() - .await - .ok_or_eyre("optimistic stream closed during startup?")? - .wrap_err("failed to get optimistic block during startup")?; - let current_block = crate::block::Current::with_optimistic(optimistic_block); - - Ok(Running { - auctions, - block_commitments, - bundles, - current_block, - executed_blocks, - optimistic_blocks, - rollup_id, - shutdown_token, - } - .into()) - } -} - -struct Running { - auctions: auction::Manager, - block_commitments: BlockCommitmentStream, - bundles: BundleStream, - current_block: crate::block::Current, - executed_blocks: ExecuteOptimisticBlockStream, - optimistic_blocks: OptimisticBlockStream, - rollup_id: RollupId, - shutdown_token: CancellationToken, -} - -impl Running { - async fn run(mut self) -> eyre::Result { - let reason: eyre::Result<&str> = { - // This is a long running loop. Errors are emitted inside the handlers. - loop { - select! { - biased; - () = self.shutdown_token.cancelled() => { - break Ok("received shutdown signal"); - }, - - Some((id, res)) = self.auctions.join_next() => { - try_break!(res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))); - }, - - res = self.optimistic_blocks.next() => { - let res = try_break!(res.ok_or_eyre("optimistic block stream closed")); - let _ = self.handle_optimistic_block(res); - }, - - res = self.block_commitments.next() => { - let res = try_break!(res.ok_or_eyre("block commitment stream closed")); - let _ = self.handle_block_commitment(res); - }, - - res = self.executed_blocks.next() => { - let res = try_break!(res.ok_or_eyre("executed block stream closed")); - let _ = self.handle_executed_block(res); - } - - Some(res) = self.bundles.next() => { - let bundle = res.wrap_err("failed to get bundle")?; - let _ = self.handle_bundle(bundle); - } - } - } - }; - - self.shutdown(reason).await - } - - #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] - fn handle_optimistic_block( - &mut self, - optimistic_block: eyre::Result, - ) -> eyre::Result<()> { - let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; - - let old_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .abort_auction(old_auction_id) - .wrap_err("failed to abort auction")?; - - info!( - optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), - "received optimistic block, aborting old auction and starting new auction" - ); - - self.current_block = crate::block::Current::with_optimistic(optimistic_block.clone()); - let new_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions.new_auction(new_auction_id); - - let base_block = crate::block::Optimistic::new(optimistic_block) - .try_into_base_block(self.rollup_id) - // FIXME: give this their proper wire names - .wrap_err("failed to create BaseBlock from FilteredSequencerBlock")?; - self.executed_blocks - .try_send(base_block) - .wrap_err("failed to forward block to execution stream")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())), err)] - fn handle_block_commitment( - &mut self, - block_commitment: eyre::Result, - ) -> eyre::Result<()> { - let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; - - if let Err(e) = self.current_block.commitment(block_commitment.clone()) { - warn!( - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - block_commitment.sequencer_block_hash = %base64(block_commitment.sequencer_block_hash()), - "received block commitment for the wrong block" - ); - return Err(e).wrap_err("failed to handle block commitment"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - - self.auctions - .start_timer(auction_id) - .wrap_err("failed to start timer")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] - fn handle_executed_block( - &mut self, - executed_block: eyre::Result, - ) -> eyre::Result<()> { - let executed_block = executed_block.wrap_err("failed to receive executed block")?; - - if let Err(e) = self.current_block.execute(executed_block.clone()) { - warn!( - // TODO: nicer display for the current block - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), - executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), - "received optimistic execution result for wrong sequencer block" - ); - return Err(e).wrap_err("failed to handle executed block"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - - self.auctions - .start_processing_bids(auction_id) - .wrap_err("failed to start processing bids")?; - - Ok(()) - } - - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] - fn handle_bundle(&mut self, bundle: crate::bundle::Bundle) -> eyre::Result<()> { - if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { - warn!( - curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - bundle.parent_rollup_block_hash = %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - return Err(e).wrap_err("failed to handle bundle"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .try_send_bundle(auction_id, bundle) - .wrap_err("failed to submit bundle to auction")?; - - Ok(()) - } - - #[instrument(skip_all)] - async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result { - const WAIT_BEFORE_ABORT: Duration = Duration::from_secs(25); - - // Necessary if we got here because of another reason than receiving an external - // shutdown signal. - self.shutdown_token.cancel(); - - let message = format!( - "waiting {} for all constituent tasks to shutdown before aborting", - humantime::format_duration(WAIT_BEFORE_ABORT), - ); - match reason { - Ok(reason) => info!(%reason, message), - Err(reason) => error!(%reason, message), - }; - let shutdown_auctions = async { - while let Some((id, res)) = self.auctions.join_next().await { - if let Err(error) = res { - // FIXME: probide a display impl for this ID - warn!(?id, %error, "auction ended with an error"); - } - } - }; - // NOTE: we don't care if this elapses. We will abort all auctions anyway - // and report if there were any still running. - let _ = tokio::time::timeout(WAIT_BEFORE_ABORT, shutdown_auctions).await; - let aborted = self.auctions.abort_all(); - if aborted > 0 { - warn!("aborted `{aborted}` auctions still running after grace period",); - } - Ok(RunState::Cancelled) - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index c803a205bf..c3bb75df45 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -18,6 +18,8 @@ use crate::{ }; mod inner; +mod running; +mod starting; /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. pub struct Auctioneer { diff --git a/crates/astria-auctioneer/src/auctioneer/running.rs b/crates/astria-auctioneer/src/auctioneer/running.rs new file mode 100644 index 0000000000..a999310fcf --- /dev/null +++ b/crates/astria-auctioneer/src/auctioneer/running.rs @@ -0,0 +1,241 @@ +use std::time::Duration; + +use astria_core::{ + primitive::v1::RollupId, + sequencerblock::v1::block::FilteredSequencerBlock, +}; +use astria_eyre::eyre::{ + self, + OptionExt as _, + WrapErr as _, +}; +use futures::StreamExt as _; +use telemetry::display::base64; +use tokio::select; +use tokio_util::sync::CancellationToken; +use tracing::{ + error, + info, + instrument, + warn, +}; + +use super::inner::RunState; +use crate::{ + auction, + rollup_channel::{ + BundleStream, + ExecuteOptimisticBlockStream, + }, + sequencer_channel::{ + BlockCommitmentStream, + OptimisticBlockStream, + }, +}; + +/// To break from a loop instead of using the `?` operator. +macro_rules! try_break { + ($res:expr) => { + match $res { + Ok(val) => val, + Err(err) => break Err(err), + } + }; +} + +pub(super) struct Running { + pub(in crate::auctioneer) auctions: crate::auction::Manager, + pub(in crate::auctioneer) block_commitments: BlockCommitmentStream, + pub(in crate::auctioneer) bundles: BundleStream, + pub(in crate::auctioneer) current_block: crate::block::Current, + pub(in crate::auctioneer) executed_blocks: ExecuteOptimisticBlockStream, + pub(in crate::auctioneer) optimistic_blocks: OptimisticBlockStream, + pub(in crate::auctioneer) rollup_id: RollupId, + pub(in crate::auctioneer) shutdown_token: CancellationToken, +} + +impl Running { + pub(super) async fn run(mut self) -> eyre::Result { + let reason: eyre::Result<&str> = { + // This is a long running loop. Errors are emitted inside the handlers. + loop { + select! { + biased; + + () = self.shutdown_token.cancelled() => { + break Ok("received shutdown signal"); + }, + + Some((id, res)) = self.auctions.join_next() => { + try_break!(res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))); + }, + + res = self.optimistic_blocks.next() => { + let res = try_break!(res.ok_or_eyre("optimistic block stream closed")); + let _ = self.handle_optimistic_block(res); + }, + + res = self.block_commitments.next() => { + let res = try_break!(res.ok_or_eyre("block commitment stream closed")); + let _ = self.handle_block_commitment(res); + }, + + res = self.executed_blocks.next() => { + let res = try_break!(res.ok_or_eyre("executed block stream closed")); + let _ = self.handle_executed_block(res); + } + + Some(res) = self.bundles.next() => { + let bundle = res.wrap_err("failed to get bundle")?; + let _ = self.handle_bundle(bundle); + } + } + } + }; + + self.shutdown(reason).await + } + + #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] + fn handle_optimistic_block( + &mut self, + optimistic_block: eyre::Result, + ) -> eyre::Result<()> { + let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; + + let old_auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions + .abort_auction(old_auction_id) + .wrap_err("failed to abort auction")?; + + info!( + optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), + "received optimistic block, aborting old auction and starting new auction" + ); + + self.current_block = crate::block::Current::with_optimistic(optimistic_block.clone()); + let new_auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions.new_auction(new_auction_id); + + let base_block = crate::block::Optimistic::new(optimistic_block) + .try_into_base_block(self.rollup_id) + // FIXME: give this their proper wire names + .wrap_err("failed to create BaseBlock from FilteredSequencerBlock")?; + self.executed_blocks + .try_send(base_block) + .wrap_err("failed to forward block to execution stream")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())), err)] + fn handle_block_commitment( + &mut self, + block_commitment: eyre::Result, + ) -> eyre::Result<()> { + let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; + + if let Err(e) = self.current_block.commitment(block_commitment.clone()) { + warn!( + current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + block_commitment.sequencer_block_hash = %base64(block_commitment.sequencer_block_hash()), + "received block commitment for the wrong block" + ); + return Err(e).wrap_err("failed to handle block commitment"); + } + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + + self.auctions + .start_timer(auction_id) + .wrap_err("failed to start timer")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + fn handle_executed_block( + &mut self, + executed_block: eyre::Result, + ) -> eyre::Result<()> { + let executed_block = executed_block.wrap_err("failed to receive executed block")?; + + if let Err(e) = self.current_block.execute(executed_block.clone()) { + warn!( + // TODO: nicer display for the current block + current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), + executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), + "received optimistic execution result for wrong sequencer block" + ); + return Err(e).wrap_err("failed to handle executed block"); + } + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + + self.auctions + .start_processing_bids(auction_id) + .wrap_err("failed to start processing bids")?; + + Ok(()) + } + + #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + fn handle_bundle(&mut self, bundle: crate::bundle::Bundle) -> eyre::Result<()> { + if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { + warn!( + curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), + bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), + bundle.parent_rollup_block_hash = %base64(bundle.parent_rollup_block_hash()), + "incoming bundle does not match current block, ignoring" + ); + return Err(e).wrap_err("failed to handle bundle"); + } + + let auction_id = + auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); + self.auctions + .try_send_bundle(auction_id, bundle) + .wrap_err("failed to submit bundle to auction")?; + + Ok(()) + } + + #[instrument(skip_all)] + async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result { + const WAIT_BEFORE_ABORT: Duration = Duration::from_secs(25); + + // Necessary if we got here because of another reason than receiving an external + // shutdown signal. + self.shutdown_token.cancel(); + + let message = format!( + "waiting {} for all constituent tasks to shutdown before aborting", + humantime::format_duration(WAIT_BEFORE_ABORT), + ); + match &reason { + Ok(reason) => info!(%reason, message), + Err(reason) => error!(%reason, message), + }; + let shutdown_auctions = async { + while let Some((id, res)) = self.auctions.join_next().await { + if let Err(error) = res { + // FIXME: probide a display impl for this ID + warn!(?id, %error, "auction ended with an error"); + } + } + }; + // NOTE: we don't care if this elapses. We will abort all auctions anyway + // and report if there were any still running. + let _ = tokio::time::timeout(WAIT_BEFORE_ABORT, shutdown_auctions).await; + let aborted = self.auctions.abort_all(); + if aborted > 0 { + warn!("aborted `{aborted}` auctions still running after grace period",); + } + reason.map(|_| RunState::Cancelled) + } +} diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/starting.rs new file mode 100644 index 0000000000..097a922c0f --- /dev/null +++ b/crates/astria-auctioneer/src/auctioneer/starting.rs @@ -0,0 +1,134 @@ +use std::time::Duration; + +use astria_core::primitive::v1::RollupId; +use astria_eyre::eyre::{ + self, + OptionExt as _, + WrapErr as _, +}; +use futures::StreamExt as _; +use tokio_util::sync::CancellationToken; + +use super::{ + inner::RunState, + running::Running, +}; +use crate::{ + rollup_channel::RollupChannel, + sequencer_channel::SequencerChannel, + Config, + Metrics, +}; + +pub(super) fn run_state( + config: Config, + shutdown_token: CancellationToken, + metrics: &'static Metrics, +) -> eyre::Result { + Starting::new(config, shutdown_token, metrics).map(Into::into) +} + +pub(super) struct Starting { + auctions: crate::auction::Manager, + rollup_channel: RollupChannel, + rollup_id: RollupId, + sequencer_channel: SequencerChannel, + shutdown_token: CancellationToken, +} + +impl Starting { + fn new( + config: Config, + shutdown_token: CancellationToken, + metrics: &'static Metrics, + ) -> eyre::Result { + let Config { + sequencer_grpc_endpoint, + sequencer_abci_endpoint, + latency_margin_ms, + rollup_grpc_endpoint, + rollup_id, + sequencer_chain_id, + sequencer_private_key_path, + sequencer_address_prefix, + fee_asset_denomination, + .. + } = config; + + let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; + let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; + + // TODO: Rearchitect this thing + let auctions = crate::auction::manager::Builder { + metrics, + shutdown_token: shutdown_token.clone(), + sequencer_grpc_endpoint: sequencer_grpc_endpoint.clone(), + sequencer_abci_endpoint, + latency_margin: Duration::from_millis(latency_margin_ms), + sequencer_private_key_path, + sequencer_address_prefix, + fee_asset_denomination, + sequencer_chain_id, + rollup_id: rollup_id.clone(), + } + .build() + .wrap_err("failed to initialize auction manager")?; + + Ok(Starting { + auctions, + rollup_channel, + rollup_id: RollupId::from_unhashed_bytes(&rollup_id), + sequencer_channel, + shutdown_token, + }) + } + + pub(super) async fn run(self) -> eyre::Result { + let Self { + auctions, + rollup_id, + rollup_channel, + mut sequencer_channel, + shutdown_token, + } = self; + + let executed_blocks = rollup_channel + .open_execute_optimistic_block_stream() + .await + .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; + + let mut optimistic_blocks = sequencer_channel + .open_get_optimistic_block_stream(rollup_id) + .await + .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; + + let block_commitments = sequencer_channel + .open_get_block_commitment_stream() + .await + .wrap_err("opening stream to receive block commitments from sequencer failed")?; + + let bundles = rollup_channel + .open_bundle_stream() + .await + .wrap_err("opening stream to receive bundles from rollup failed")?; + + let optimistic_block = optimistic_blocks + .next() + .await + .ok_or_eyre("optimistic stream closed during startup?")? + .wrap_err("failed to get optimistic block during startup")?; + let current_block = crate::block::Current::with_optimistic(optimistic_block); + + Ok(Running { + auctions, + block_commitments, + bundles, + current_block, + executed_blocks, + optimistic_blocks, + rollup_id, + shutdown_token, + } + .into()) + } +} From 3a0355a58039994ceb66c41cd43bf08161324264 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 12:16:49 +0100 Subject: [PATCH 25/73] remove unnecessary exclusive ref --- crates/astria-auctioneer/src/auctioneer/starting.rs | 2 +- crates/astria-auctioneer/src/sequencer_channel.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/starting.rs index 097a922c0f..052424987c 100644 --- a/crates/astria-auctioneer/src/auctioneer/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/starting.rs @@ -88,7 +88,7 @@ impl Starting { auctions, rollup_id, rollup_channel, - mut sequencer_channel, + sequencer_channel, shutdown_token, } = self; diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 2217f2587e..13654a49ff 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -51,7 +51,7 @@ impl SequencerChannel { } pub(crate) async fn open_get_block_commitment_stream( - &mut self, + &self, ) -> eyre::Result { use astria_core::generated::sequencerblock::optimisticblock::v1alpha1:: optimistic_block_service_client::OptimisticBlockServiceClient; @@ -65,7 +65,7 @@ impl SequencerChannel { } pub(crate) async fn open_get_optimistic_block_stream( - &mut self, + &self, rollup_id: RollupId, ) -> eyre::Result { use astria_core::generated::sequencerblock::optimisticblock::v1alpha1::{ From 172bf53eb6d51489188091ed7b5e346bdcf49f18 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 15:43:55 +0100 Subject: [PATCH 26/73] run all startup initialization futures concurrently --- .../src/auctioneer/starting.rs | 106 +++++++++++++----- .../astria-auctioneer/src/rollup_channel.rs | 1 + .../src/sequencer_channel.rs | 1 + 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/starting.rs index 052424987c..d63f7f17c5 100644 --- a/crates/astria-auctioneer/src/auctioneer/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/starting.rs @@ -7,6 +7,7 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::StreamExt as _; +use tokio::select; use tokio_util::sync::CancellationToken; use super::{ @@ -14,8 +15,16 @@ use super::{ running::Running, }; use crate::{ - rollup_channel::RollupChannel, - sequencer_channel::SequencerChannel, + rollup_channel::{ + BundleStream, + ExecuteOptimisticBlockStream, + RollupChannel, + }, + sequencer_channel::{ + BlockCommitmentStream, + OptimisticBlockStream, + SequencerChannel, + }, Config, Metrics, }; @@ -84,41 +93,31 @@ impl Starting { } pub(super) async fn run(self) -> eyre::Result { + select!( + biased; + + () = self.shutdown_token.clone().cancelled_owned() => Ok(RunState::Cancelled), + res = self.start_running() => res, + ) + } + + async fn start_running(self) -> eyre::Result { let Self { auctions, - rollup_id, rollup_channel, + rollup_id, sequencer_channel, shutdown_token, } = self; - - let executed_blocks = rollup_channel - .open_execute_optimistic_block_stream() - .await - .wrap_err("opening stream to execute optimistic blocks on rollup failed")?; - - let mut optimistic_blocks = sequencer_channel - .open_get_optimistic_block_stream(rollup_id) - .await - .wrap_err("opening stream to receive optimistic blocks from sequencer failed")?; - - let block_commitments = sequencer_channel - .open_get_block_commitment_stream() - .await - .wrap_err("opening stream to receive block commitments from sequencer failed")?; - - let bundles = rollup_channel - .open_bundle_stream() - .await - .wrap_err("opening stream to receive bundles from rollup failed")?; - - let optimistic_block = optimistic_blocks - .next() - .await - .ok_or_eyre("optimistic stream closed during startup?")? - .wrap_err("failed to get optimistic block during startup")?; - let current_block = crate::block::Current::with_optimistic(optimistic_block); - + let (bundles, executed_blocks, block_commitments, (optimistic_blocks, current_block)) = tokio::try_join!( + open_bundle_stream(rollup_channel.clone()), + open_execute_optimistic_block_stream(rollup_channel.clone()), + open_block_commitment_stream(sequencer_channel.clone()), + open_optimistic_block_stream_and_get_current_block( + sequencer_channel.clone(), + rollup_id + ), + )?; Ok(Running { auctions, block_commitments, @@ -132,3 +131,48 @@ impl Starting { .into()) } } + +async fn open_optimistic_block_stream_and_get_current_block( + chan: SequencerChannel, + rollup_id: RollupId, +) -> eyre::Result<(OptimisticBlockStream, crate::block::Current)> { + let mut the_stream = chan + .open_get_optimistic_block_stream(rollup_id) + .await + .wrap_err_with(|| { + format!( + "failed to open optimistic block stream to Sequencer node for rollup ID \ + `{rollup_id}`" + ) + })?; + let optimistic_block = the_stream + .next() + .await + .ok_or_eyre("optimistic block stream closed before yielding the current block")? + .wrap_err( + "failed to get current optimistic block after opening a stream to the Sequencer node", + )?; + let current_block = crate::block::Current::with_optimistic(optimistic_block); + Ok((the_stream, current_block)) +} + +async fn open_block_commitment_stream( + chan: SequencerChannel, +) -> eyre::Result { + chan.open_get_block_commitment_stream() + .await + .wrap_err("failed to open block commitment stream to sequencer node") +} + +async fn open_bundle_stream(chan: RollupChannel) -> eyre::Result { + chan.open_bundle_stream() + .await + .wrap_err("failed to open `bundle stream` to rollup node") +} +async fn open_execute_optimistic_block_stream( + chan: RollupChannel, +) -> eyre::Result { + chan.open_execute_optimistic_block_stream() + .await + .wrap_err("failed to open `execute optimistic block stream` to rollup node") +} diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index f37e1fa88e..1bba00af40 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -27,6 +27,7 @@ pub(crate) fn open(endpoint: &str) -> eyre::Result { .wrap_err_with(|| format!("failed to create a gRPC channel to rollup at `{endpoint}`")) } +#[derive(Clone)] pub(crate) struct RollupChannel { inner: Channel, } diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 13654a49ff..1269e81df9 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -33,6 +33,7 @@ pub(crate) fn open(endpoint: &str) -> eyre::Result { .wrap_err_with(|| format!("failed to create a gRPC channel to Sequencer at `{endpoint}`")) } +#[derive(Clone)] pub(crate) struct SequencerChannel { inner: Channel, } From bf83e2b04a6d822aa14fa031103dd53deba9a432 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 15:54:53 +0100 Subject: [PATCH 27/73] auctioneer is unbiased toward all events we only to bias toward the cancellation token. all other events should be dealt with using the same (pseudorandom) selector. --- .../src/auctioneer/running.rs | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/running.rs b/crates/astria-auctioneer/src/auctioneer/running.rs index a999310fcf..31567fd317 100644 --- a/crates/astria-auctioneer/src/auctioneer/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/running.rs @@ -33,16 +33,6 @@ use crate::{ }, }; -/// To break from a loop instead of using the `?` operator. -macro_rules! try_break { - ($res:expr) => { - match $res { - Ok(val) => val, - Err(err) => break Err(err), - } - }; -} - pub(super) struct Running { pub(in crate::auctioneer) auctions: crate::auction::Manager, pub(in crate::auctioneer) block_commitments: BlockCommitmentStream, @@ -62,32 +52,14 @@ impl Running { select! { biased; - () = self.shutdown_token.cancelled() => { + () = self.shutdown_token.clone().cancelled_owned() => { break Ok("received shutdown signal"); }, - Some((id, res)) = self.auctions.join_next() => { - try_break!(res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))); - }, - - res = self.optimistic_blocks.next() => { - let res = try_break!(res.ok_or_eyre("optimistic block stream closed")); - let _ = self.handle_optimistic_block(res); - }, - - res = self.block_commitments.next() => { - let res = try_break!(res.ok_or_eyre("block commitment stream closed")); - let _ = self.handle_block_commitment(res); - }, - - res = self.executed_blocks.next() => { - let res = try_break!(res.ok_or_eyre("executed block stream closed")); - let _ = self.handle_executed_block(res); - } - - Some(res) = self.bundles.next() => { - let bundle = res.wrap_err("failed to get bundle")?; - let _ = self.handle_bundle(bundle); + res = self.handle_event() => { + if let Err(err) = res { + break Err(err); + } } } } @@ -96,6 +68,35 @@ impl Running { self.shutdown(reason).await } + async fn handle_event(&mut self) -> eyre::Result<()> { + select!( + Some((id, res)) = self.auctions.join_next() => { + res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))?; + }, + + res = self.optimistic_blocks.next() => { + let res = res.ok_or_eyre("optimistic block stream closed")?; + let _ = self.handle_optimistic_block(res); + }, + + res = self.block_commitments.next() => { + let res = res.ok_or_eyre("block commitment stream closed")?; + let _ = self.handle_block_commitment(res); + }, + + res = self.executed_blocks.next() => { + let res = res.ok_or_eyre("executed block stream closed")?; + let _ = self.handle_executed_block(res); + } + + Some(res) = self.bundles.next() => { + let bundle = res.wrap_err("failed to get bundle")?; + let _ = self.handle_bundle(bundle); + } + ); + Ok(()) + } + #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] fn handle_optimistic_block( &mut self, From 2501b8f0a2c8c5bb9f7cdce37e2b35789458f181 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 16:22:40 +0100 Subject: [PATCH 28/73] treat handling of bundle events like all other events --- crates/astria-auctioneer/src/auctioneer/running.rs | 6 +++--- crates/astria-auctioneer/src/rollup_channel.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/running.rs b/crates/astria-auctioneer/src/auctioneer/running.rs index 31567fd317..d24e8680f6 100644 --- a/crates/astria-auctioneer/src/auctioneer/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/running.rs @@ -90,8 +90,7 @@ impl Running { } Some(res) = self.bundles.next() => { - let bundle = res.wrap_err("failed to get bundle")?; - let _ = self.handle_bundle(bundle); + let _ = self.handle_bundle(res); } ); Ok(()) @@ -186,7 +185,8 @@ impl Running { } #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] - fn handle_bundle(&mut self, bundle: crate::bundle::Bundle) -> eyre::Result<()> { + fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { + let bundle = bundle.wrap_err("received problematic bundle")?; if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { warn!( curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 1bba00af40..1988014b06 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -100,7 +100,7 @@ impl Stream for BundleStream { }; let raw = res - .wrap_err("failed receiving streamed message from server")? + .wrap_err("error while receiving streamed message from server")? .bundle .ok_or_else(|| { eyre!( @@ -111,7 +111,7 @@ impl Stream for BundleStream { let bundle = Bundle::try_from_raw(raw).wrap_err_with(|| { format!( - "failed to validate received `{}`", + "failed to validate received message `{}`", astria_core::generated::bundle::v1alpha1::Bundle::full_name() ) })?; From 453aedab169b147c4b0b79b6fbc358ca4a259d1e Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 17:10:28 +0100 Subject: [PATCH 29/73] use the channel singleton to pass a sequencer service client to auction manager --- crates/astria-auctioneer/src/auction/manager.rs | 12 +++--------- .../astria-auctioneer/src/auctioneer/running.rs | 3 ++- .../src/auctioneer/starting.rs | 2 +- .../astria-auctioneer/src/sequencer_channel.rs | 17 ++++++++++++----- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index d422caa5a8..bbe18c3700 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -18,7 +18,7 @@ use tokio_util::{ sync::CancellationToken, task::JoinMap, }; -use tonic::transport::Endpoint; +use tonic::transport::Channel; use tracing::{ info, instrument, @@ -37,7 +37,7 @@ pub(crate) struct Builder { pub(crate) shutdown_token: CancellationToken, /// The gRPC endpoint for the sequencer service used by auctions. - pub(crate) sequencer_grpc_endpoint: String, + pub(crate) sequencer_grpc_client: SequencerServiceClient, /// The ABCI endpoint for the sequencer service used by auctions. pub(crate) sequencer_abci_endpoint: String, /// The amount of time to run the auction timer for. @@ -59,7 +59,7 @@ impl Builder { let Self { metrics, shutdown_token, - sequencer_grpc_endpoint, + sequencer_grpc_client, sequencer_abci_endpoint, latency_margin, fee_asset_denomination, @@ -76,12 +76,6 @@ impl Builder { .wrap_err("failed to load sequencer private key")?; info!(address = %sequencer_key.address(), "loaded sequencer signer"); - let sequencer_grpc_uri: tonic::transport::Uri = sequencer_grpc_endpoint - .parse() - .wrap_err("failed to parse sequencer grpc endpoint as URI")?; - let sequencer_grpc_client = - SequencerServiceClient::new(Endpoint::from(sequencer_grpc_uri).connect_lazy()); - let sequencer_abci_client = sequencer_client::HttpClient::new(sequencer_abci_endpoint.as_str()) .wrap_err("failed constructing sequencer abci client")?; diff --git a/crates/astria-auctioneer/src/auctioneer/running.rs b/crates/astria-auctioneer/src/auctioneer/running.rs index d24e8680f6..c0cb7fb485 100644 --- a/crates/astria-auctioneer/src/auctioneer/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/running.rs @@ -101,7 +101,8 @@ impl Running { &mut self, optimistic_block: eyre::Result, ) -> eyre::Result<()> { - let optimistic_block = optimistic_block.wrap_err("failed to receive optimistic block")?; + let optimistic_block = + optimistic_block.wrap_err("encountered problems receiving optimistic block message")?; let old_auction_id = auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/starting.rs index d63f7f17c5..22dafa31ca 100644 --- a/crates/astria-auctioneer/src/auctioneer/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/starting.rs @@ -71,7 +71,7 @@ impl Starting { let auctions = crate::auction::manager::Builder { metrics, shutdown_token: shutdown_token.clone(), - sequencer_grpc_endpoint: sequencer_grpc_endpoint.clone(), + sequencer_grpc_client: sequencer_channel.to_sequencer_service_client(), sequencer_abci_endpoint, latency_margin: Duration::from_millis(latency_margin_ms), sequencer_private_key_path, diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 1269e81df9..9fc3b476ff 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -4,11 +4,14 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::optimisticblock::v1alpha1::{ - GetBlockCommitmentStreamRequest, - GetBlockCommitmentStreamResponse, - GetOptimisticBlockStreamRequest, - GetOptimisticBlockStreamResponse, + generated::sequencerblock::{ + optimisticblock::v1alpha1::{ + GetBlockCommitmentStreamRequest, + GetBlockCommitmentStreamResponse, + GetOptimisticBlockStreamRequest, + GetOptimisticBlockStreamResponse, + }, + v1::sequencer_service_client::SequencerServiceClient, }, primitive::v1::RollupId, sequencerblock::v1::block::FilteredSequencerBlock, @@ -51,6 +54,10 @@ impl SequencerChannel { }) } + pub(crate) fn to_sequencer_service_client(&self) -> SequencerServiceClient { + SequencerServiceClient::new(self.inner.clone()) + } + pub(crate) async fn open_get_block_commitment_stream( &self, ) -> eyre::Result { From c857188b4c2b32db6922b062e94b949b6564336f Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 25 Nov 2024 17:12:37 +0100 Subject: [PATCH 30/73] use the same rollup ID everywhere --- crates/astria-auctioneer/src/auction/manager.rs | 4 +--- crates/astria-auctioneer/src/auctioneer/starting.rs | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index bbe18c3700..6dcf0d2114 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -51,7 +51,7 @@ pub(crate) struct Builder { /// The chain ID for sequencer transactions pub(crate) sequencer_chain_id: String, /// The rollup ID for the `RollupDataSubmission`s with auction results - pub(crate) rollup_id: String, + pub(crate) rollup_id: RollupId, } impl Builder { @@ -80,8 +80,6 @@ impl Builder { sequencer_client::HttpClient::new(sequencer_abci_endpoint.as_str()) .wrap_err("failed constructing sequencer abci client")?; - let rollup_id = RollupId::from_unhashed_bytes(&rollup_id); - Ok(Manager { metrics, shutdown_token, diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/starting.rs index 22dafa31ca..17aa912547 100644 --- a/crates/astria-auctioneer/src/auctioneer/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/starting.rs @@ -64,6 +64,7 @@ impl Starting { .. } = config; + let rollup_id = RollupId::from_unhashed_bytes(rollup_id); let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; @@ -78,7 +79,7 @@ impl Starting { sequencer_address_prefix, fee_asset_denomination, sequencer_chain_id, - rollup_id: rollup_id.clone(), + rollup_id, } .build() .wrap_err("failed to initialize auction manager")?; @@ -86,7 +87,7 @@ impl Starting { Ok(Starting { auctions, rollup_channel, - rollup_id: RollupId::from_unhashed_bytes(&rollup_id), + rollup_id, sequencer_channel, shutdown_token, }) From b78e186ce6039c0cb0b0cb9d2a1181df9780a03c Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 26 Nov 2024 00:06:27 +0100 Subject: [PATCH 31/73] make auctioneer run states private to the inner business logic impl --- .../src/auctioneer/{inner.rs => inner/mod.rs} | 14 ++++++++------ .../src/auctioneer/{ => inner}/running.rs | 18 +++++++++--------- .../src/auctioneer/{ => inner}/starting.rs | 4 ++-- crates/astria-auctioneer/src/auctioneer/mod.rs | 2 -- 4 files changed, 19 insertions(+), 19 deletions(-) rename crates/astria-auctioneer/src/auctioneer/{inner.rs => inner/mod.rs} (91%) rename crates/astria-auctioneer/src/auctioneer/{ => inner}/running.rs (94%) rename crates/astria-auctioneer/src/auctioneer/{ => inner}/starting.rs (99%) diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs similarity index 91% rename from crates/astria-auctioneer/src/auctioneer/inner.rs rename to crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 19519d9d52..d705a04129 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -4,15 +4,17 @@ use astria_eyre::eyre::{ }; use tokio_util::sync::CancellationToken; -use super::{ - running::Running, - starting::Starting, -}; use crate::{ Config, Metrics, }; +mod running; +mod starting; + +use running::Running; +use starting::Starting; + /// The implementation of the auctioneer business logic. pub(super) struct Inner { run_state: RunState, @@ -25,7 +27,7 @@ impl Inner { metrics: &'static Metrics, shutdown_token: CancellationToken, ) -> eyre::Result { - let run_state = super::starting::run_state(cfg, shutdown_token, metrics) + let run_state = starting::run_state(cfg, shutdown_token, metrics) .wrap_err("failed initializating in starting state")?; Ok(Self { run_state, @@ -55,7 +57,7 @@ impl Inner { } } -pub(super) enum RunState { +enum RunState { Cancelled, Starting(Starting), Running(Running), diff --git a/crates/astria-auctioneer/src/auctioneer/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/running.rs similarity index 94% rename from crates/astria-auctioneer/src/auctioneer/running.rs rename to crates/astria-auctioneer/src/auctioneer/inner/running.rs index c0cb7fb485..53d01fd0f4 100644 --- a/crates/astria-auctioneer/src/auctioneer/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/running.rs @@ -20,7 +20,7 @@ use tracing::{ warn, }; -use super::inner::RunState; +use super::RunState; use crate::{ auction, rollup_channel::{ @@ -34,14 +34,14 @@ use crate::{ }; pub(super) struct Running { - pub(in crate::auctioneer) auctions: crate::auction::Manager, - pub(in crate::auctioneer) block_commitments: BlockCommitmentStream, - pub(in crate::auctioneer) bundles: BundleStream, - pub(in crate::auctioneer) current_block: crate::block::Current, - pub(in crate::auctioneer) executed_blocks: ExecuteOptimisticBlockStream, - pub(in crate::auctioneer) optimistic_blocks: OptimisticBlockStream, - pub(in crate::auctioneer) rollup_id: RollupId, - pub(in crate::auctioneer) shutdown_token: CancellationToken, + pub(super) auctions: crate::auction::Manager, + pub(super) block_commitments: BlockCommitmentStream, + pub(super) bundles: BundleStream, + pub(super) current_block: crate::block::Current, + pub(super) executed_blocks: ExecuteOptimisticBlockStream, + pub(super) optimistic_blocks: OptimisticBlockStream, + pub(super) rollup_id: RollupId, + pub(super) shutdown_token: CancellationToken, } impl Running { diff --git a/crates/astria-auctioneer/src/auctioneer/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs similarity index 99% rename from crates/astria-auctioneer/src/auctioneer/starting.rs rename to crates/astria-auctioneer/src/auctioneer/inner/starting.rs index 17aa912547..084da0d923 100644 --- a/crates/astria-auctioneer/src/auctioneer/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs @@ -11,8 +11,8 @@ use tokio::select; use tokio_util::sync::CancellationToken; use super::{ - inner::RunState, - running::Running, + RunState, + Running, }; use crate::{ rollup_channel::{ diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index c3bb75df45..c803a205bf 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -18,8 +18,6 @@ use crate::{ }; mod inner; -mod running; -mod starting; /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. pub struct Auctioneer { From 51e4382f69839053459f4e58ab2b137a8c135203 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 26 Nov 2024 15:03:07 +0100 Subject: [PATCH 32/73] impl display for auction IDs and use that instead of explicit base64 --- Cargo.lock | 1 + crates/astria-auctioneer/Cargo.toml | 1 + crates/astria-auctioneer/src/auction/mod.rs | 21 +++++++++++-------- .../src/auctioneer/inner/running.rs | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20ab3837f6..94bd5a6cbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ dependencies = [ "astria-eyre", "astria-sequencer-client", "astria-telemetry", + "base64 0.21.7", "bytes", "futures", "hex", diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 9ea944e461..4f2342baa8 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -39,6 +39,7 @@ tracing = { workspace = true, features = ["attributes"] } tryhard = { workspace = true } tonic = { workspace = true } tokio-stream = { workspace = true, features = ["net"] } +base64.workspace = true [dev-dependencies] config = { package = "astria-config", path = "../astria-config", features = [ diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auction/mod.rs index 208a9c862e..0e0c5f4b30 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auction/mod.rs @@ -64,7 +64,6 @@ use sequencer_client::{ Address, SequencerClientExt, }; -use telemetry::display::base64; use tokio::{ select, sync::mpsc, @@ -96,9 +95,13 @@ impl Id { } } -impl AsRef<[u8]> for Id { - fn as_ref(&self) -> &[u8] { - &self.0 +impl std::fmt::Display for Id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use base64::{ + display::Base64Display, + engine::general_purpose::STANDARD, + }; + Base64Display::new(self.0.as_ref(), &STANDARD).fmt(f) } } @@ -201,7 +204,7 @@ impl Auction { match cmd { Command::Abort => { // abort the auction early - break Err(eyre!("auction {id} received abort signal", id = base64(&self.id))); + break Err(eyre!("auction {} received abort signal", self.id)); }, Command::StartProcessingBids => { if auction_is_open { @@ -230,13 +233,13 @@ impl Auction { Some(bundle) = self.new_bundles_rx.recv(), if auction_is_open => { if allocation_rule.bid(bundle.clone()) { info!( - auction.id = %base64(self.id), + auction.id = %self.id, bundle.bid = %bundle.bid(), "received new highest bid" ); } else { debug!( - auction.id = %base64(self.id), + auction.id = %self.id, bundle.bid = %bundle.bid(), "received bid lower than current highest bid, discarding" ); @@ -283,11 +286,11 @@ impl Auction { match result { Ok(resp) => { // TODO: handle failed submission instead of just logging the result - info!(auction.id = %base64(self.id), auction.result = %resp.log, "auction result submitted to sequencer"); + info!(auction.id = %self.id, auction.result = %resp.log, "auction result submitted to sequencer"); Ok(()) }, Err(e) => { - error!(auction.id = %base64(self.id), err = %e, "failed to submit auction result to sequencer"); + error!(auction.id = %self.id, err = %e, "failed to submit auction result to sequencer"); Err(e).wrap_err("failed to submit auction result to sequencer") }, } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/running.rs index 53d01fd0f4..eef8e03dfa 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/running.rs @@ -71,7 +71,7 @@ impl Running { async fn handle_event(&mut self) -> eyre::Result<()> { select!( Some((id, res)) = self.auctions.join_next() => { - res.wrap_err_with(|| format!("auction failed for block `{}`", base64(id)))?; + res.wrap_err_with(|| format!("auction failed for block `{id}`"))?; }, res = self.optimistic_blocks.next() => { From c18d87ca1a27c1a94c916ee8632ea58ddb0e4771 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 26 Nov 2024 17:42:34 +0100 Subject: [PATCH 33/73] abort auctions using its cancellation token Each auction already has a cancellation token, so sending an extra command over its mpsc channel is a) unncessary, and b) would lead to it potentially not receiving the shutdown signal because it's congested. --- .../astria-auctioneer/src/auction/builder.rs | 7 +++--- .../astria-auctioneer/src/auction/manager.rs | 13 ++++------ crates/astria-auctioneer/src/auction/mod.rs | 24 +++++++------------ 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/builder.rs b/crates/astria-auctioneer/src/auction/builder.rs index 9038fb76f9..3bcb28adf2 100644 --- a/crates/astria-auctioneer/src/auction/builder.rs +++ b/crates/astria-auctioneer/src/auction/builder.rs @@ -20,7 +20,7 @@ use crate::Metrics; pub(crate) struct Builder { pub(crate) metrics: &'static Metrics, - pub(crate) shutdown_token: CancellationToken, + pub(crate) cancellation_token: CancellationToken, /// The endpoint for the sequencer gRPC service used to get pending nonces pub(crate) sequencer_grpc_client: SequencerServiceClient, @@ -45,7 +45,7 @@ impl Builder { pub(crate) fn build(self) -> (Handle, Auction) { let Self { metrics, - shutdown_token, + cancellation_token, sequencer_grpc_client, sequencer_abci_client, latency_margin, @@ -62,7 +62,7 @@ impl Builder { let auction = Auction { metrics, - shutdown_token, + cancellation_token: cancellation_token.clone(), sequencer_grpc_client, sequencer_abci_client, commands_rx, @@ -77,6 +77,7 @@ impl Builder { ( Handle { + cancellation_token, commands_tx, new_bundles_tx, }, diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index 6dcf0d2114..c20ac537a1 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -82,7 +82,7 @@ impl Builder { Ok(Manager { metrics, - shutdown_token, + cancellation_token: shutdown_token, sequencer_grpc_client, sequencer_abci_client, latency_margin, @@ -98,7 +98,7 @@ impl Builder { pub(crate) struct Manager { metrics: &'static crate::Metrics, - shutdown_token: CancellationToken, + cancellation_token: CancellationToken, sequencer_grpc_client: SequencerServiceClient, sequencer_abci_client: sequencer_client::HttpClient, latency_margin: std::time::Duration, @@ -115,7 +115,7 @@ impl Manager { pub(crate) fn new_auction(&mut self, auction_id: Id) { let (handle, auction) = super::Builder { metrics: self.metrics, - shutdown_token: self.shutdown_token.child_token(), + cancellation_token: self.cancellation_token.child_token(), sequencer_grpc_client: self.sequencer_grpc_client.clone(), sequencer_abci_client: self.sequencer_abci_client.clone(), latency_margin: self.latency_margin, @@ -135,12 +135,9 @@ impl Manager { pub(crate) fn abort_auction(&mut self, auction_id: Id) -> eyre::Result<()> { let handle = self .auction_handles - .get_mut(&auction_id) + .get(&auction_id) .ok_or_eyre("unable to get handle for the given auction")?; - - handle - .try_abort() - .wrap_err("failed to send command to abort auction")?; + handle.cancel(); Ok(()) } diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auction/mod.rs index 0e0c5f4b30..c3c06c2ad1 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auction/mod.rs @@ -9,8 +9,8 @@ //! submit it to the sequencer. //! //! ## Aborting an Auction -//! The auction may also be aborted at any point before the timer expires by receiving a -//! `Command::Abort`. This will cause the auction to return early without submitting a winner, +//! The auction may also be aborted at any point before the timer expires. +//! This will cause the auction to return early without submitting a winner, //! effectively discarding any bundles that were processed. //! This is used for leveraging optimsitic execution, running an auction for block data that has //! been proposed in the sequencer network's cometBFT but not yet finalized. @@ -112,21 +112,17 @@ mod allocation_rule; enum Command { StartProcessingBids, StartTimer, - Abort, } pub(crate) struct Handle { + cancellation_token: CancellationToken, commands_tx: mpsc::Sender, new_bundles_tx: mpsc::Sender, } impl Handle { - pub(crate) fn try_abort(&mut self) -> eyre::Result<()> { - self.commands_tx - .try_send(Command::Abort) - .wrap_err("unable to send abort command to auction")?; - - Ok(()) + pub(crate) fn cancel(&self) { + self.cancellation_token.cancel(); } pub(crate) fn start_processing_bids(&mut self) -> eyre::Result<()> { @@ -156,7 +152,7 @@ impl Handle { pub(crate) struct Auction { #[allow(dead_code)] metrics: &'static Metrics, - shutdown_token: CancellationToken, + cancellation_token: CancellationToken, /// The sequencer's gRPC client, used for fetching pending nonces sequencer_grpc_client: SequencerServiceClient, @@ -193,7 +189,7 @@ impl Auction { select! { biased; - () = self.shutdown_token.cancelled() => break Err(eyre!("received shutdown signal")), + () = self.cancellation_token.cancelled() => break Err(eyre!("received shutdown signal")), // get the auction winner when the timer expires _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { @@ -202,10 +198,6 @@ impl Auction { Some(cmd) = self.commands_rx.recv() => { match cmd { - Command::Abort => { - // abort the auction early - break Err(eyre!("auction {} received abort signal", self.id)); - }, Command::StartProcessingBids => { if auction_is_open { break Err(eyre!("auction received signal to start processing bids twice")); @@ -279,7 +271,7 @@ impl Auction { biased; // TODO: should this be Ok(())? or Ok("received shutdown signal")? - () = self.shutdown_token.cancelled() => Err(eyre!("received shutdown signal during auction result submission")), + () = self.cancellation_token.cancelled() => Err(eyre!("received shutdown signal during auction result submission")), result = submit_transaction(self.sequencer_abci_client.clone(), transaction, self.metrics) => { // TODO: how to handle submission failure better? From ca5281e6b75fab02a3cf5523df21f7ef67c89660 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 27 Nov 2024 13:01:38 +0100 Subject: [PATCH 34/73] move tracking of current blocks into the auction manager the inner auctioneer logic (i.e., the broker-like select-loop) need not know about the status of the running auctions. The auction manager is the correct place to track it. --- .../astria-auctioneer/src/auction/manager.rs | 174 +++++++++++++++--- .../src/auctioneer/inner/running.rs | 83 ++------- .../src/auctioneer/inner/starting.rs | 28 +-- crates/astria-auctioneer/src/block/mod.rs | 61 +++--- 4 files changed, 203 insertions(+), 143 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index c20ac537a1..4634262e67 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -8,6 +8,7 @@ use astria_core::{ asset, RollupId, }, + sequencerblock::v1::block::FilteredSequencerBlock, }; use astria_eyre::eyre::{ self, @@ -22,6 +23,7 @@ use tonic::transport::Channel; use tracing::{ info, instrument, + warn, }; use super::{ @@ -30,7 +32,10 @@ use super::{ Id, SequencerKey, }; -use crate::flatten_join_result; +use crate::{ + block::Commitment, + flatten_join_result, +}; pub(crate) struct Builder { pub(crate) metrics: &'static crate::Metrics, @@ -92,6 +97,8 @@ impl Builder { fee_asset_denomination, sequencer_chain_id, rollup_id, + + current_block: None, }) } } @@ -102,24 +109,50 @@ pub(crate) struct Manager { sequencer_grpc_client: SequencerServiceClient, sequencer_abci_client: sequencer_client::HttpClient, latency_margin: std::time::Duration, + // FIXME: Having a joinmap here is actually weird: if a new block arrives + // the old auction should always be nuked. Either the optimistic block was + // replaced (proposed block rejected), or a new block is being built (auctioneer + // failed to submit the winning allocation in time). running_auctions: JoinMap>, auction_handles: HashMap, sequencer_key: SequencerKey, fee_asset_denomination: asset::Denom, sequencer_chain_id: String, rollup_id: RollupId, + current_block: Option, } impl Manager { + // pub(crate) fn new_auction(&mut self, auction_id: Id) { + // TODO: Add some better instrumentation. #[instrument(skip(self))] - pub(crate) fn new_auction(&mut self, auction_id: Id) { + pub(crate) fn new_auction(&mut self, block: FilteredSequencerBlock) { + let new_auction_id = crate::auction::Id::from_sequencer_block_hash(*block.block_hash()); + + if let Some(old_block) = self + .current_block + .replace(crate::block::Current::with_optimistic(block)) + { + // TODO: Track the ID in the "current block" (or get rid of it altogether?) + let old_auction_id = + crate::auction::Id::from_sequencer_block_hash(old_block.sequencer_block_hash()); + info!( + %new_auction_id, + %old_auction_id, + "received optimistic block, aborting old auction and starting new auction" + ); + + // TODO: provide feedback if the auction didn't exist?; + let _ = self.abort_auction(old_auction_id); + } + let (handle, auction) = super::Builder { metrics: self.metrics, cancellation_token: self.cancellation_token.child_token(), sequencer_grpc_client: self.sequencer_grpc_client.clone(), sequencer_abci_client: self.sequencer_abci_client.clone(), latency_margin: self.latency_margin, - auction_id, + auction_id: new_auction_id, sequencer_key: self.sequencer_key.clone(), fee_asset_denomination: self.fee_asset_denomination.clone(), sequencer_chain_id: self.sequencer_chain_id.clone(), @@ -128,11 +161,11 @@ impl Manager { .build(); // spawn and save handle - self.running_auctions.spawn(auction_id, auction.run()); - self.auction_handles.insert(auction_id, handle); + self.running_auctions.spawn(new_auction_id, auction.run()); + self.auction_handles.insert(new_auction_id, handle); } - pub(crate) fn abort_auction(&mut self, auction_id: Id) -> eyre::Result<()> { + fn abort_auction(&mut self, auction_id: Id) -> eyre::Result<()> { let handle = self .auction_handles .get(&auction_id) @@ -142,38 +175,123 @@ impl Manager { } #[instrument(skip(self))] - pub(crate) fn start_timer(&mut self, auction_id: Id) -> eyre::Result<()> { - let handle = self - .auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")?; + // pub(crate) fn start_timer(&mut self, auction_id: Id) -> eyre::Result<()> { + pub(crate) fn start_timer(&mut self, block_commitment: Commitment) -> eyre::Result<()> { + let auction_id = + crate::auction::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); - handle - .start_timer() - .wrap_err("failed to send command to start timer to auction")?; + if let Some(current_block) = &mut self.current_block { + if current_block.commitment(block_commitment) { + let handle = self + .auction_handles + .get_mut(&auction_id) + .ok_or_eyre("unable to get handle for the given auction")?; + handle + .start_timer() + .wrap_err("failed to send command to start timer to auction")?; + } else { + // TODO: provide better information on the blocks/currently running auction. + // warn!( + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // block_commitment.sequencer_block_hash = + // %base64(block_commitment.sequencer_block_hash()), "received + // block commitment for the wrong block" ); + info!( + "not starting the auction timer because the sequencer block hash of the \ + commitment hash did not match + not match that of the currently running auction" + ); + } + } else { + info!( + "cannot start the auction timer with the received executed block because no \ + auction was currently running; dropping the commit block", + ); + } Ok(()) } #[instrument(skip(self))] - pub(crate) fn start_processing_bids(&mut self, auction_id: Id) -> eyre::Result<()> { - let handle = self - .auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")?; + // pub(crate) fn start_processing_bids(&mut self, auction_id: Id) -> eyre::Result<()> { + pub(crate) fn start_processing_bids( + &mut self, + block: crate::block::Executed, + ) -> eyre::Result<()> { + let auction_id = + crate::auction::Id::from_sequencer_block_hash(block.sequencer_block_hash()); + + if let Some(current_block) = &mut self.current_block { + if !current_block.execute(block) { + // TODO: bring back the fields to track the dropped block and current block + // warn!( + // // TODO: nicer display for the current block + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // executed_block.sequencer_block_hash = + // %base64(executed_block.sequencer_block_hash()), + // executed_block.rollup_block_hash = + // %base64(executed_block.rollup_block_hash()), "received + // optimistic execution result for wrong sequencer block" ); + warn!( + "not starting to process bids in the current auction because we received an \ + executed block from the rollup with a sequencer block hash that does not \ + match that of the currently running auction; dropping the executed block" + ); + } else { + let handle = self + .auction_handles + .get_mut(&auction_id) + .ok_or_eyre("unable to get handle for the given auction")?; - handle - .start_processing_bids() - .wrap_err("failed to send command to start processing bids")?; + handle + .start_processing_bids() + .wrap_err("failed to send command to start processing bids")?; + } + } else { + info!( + "cannot start processing bids with the received executed block because no auction \ + was currently running; dropping the executed block" + ); + } Ok(()) } - pub(crate) fn try_send_bundle(&mut self, auction_id: Id, bundle: Bundle) -> eyre::Result<()> { - self.auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")? - .try_send_bundle(bundle) - .wrap_err("failed to add bundle to auction") + pub(crate) fn forward_bundle_to_auction(&mut self, bundle: Bundle) -> eyre::Result<()> { + let auction_id = + crate::auction::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); + if let Some(current_block) = &mut self.current_block { + if let Err(e) = current_block + .ensure_bundle_is_valid(&bundle) + .wrap_err("failed to handle bundle") + { + warn!( + // TODO: Add these fields back in. Is it even necessary to return the error? + // Can't we just fire the event here? necessary? + // + // curent_block.sequencer_block_hash = %base64(self. + // current_block.sequencer_block_hash()), + // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), + // bundle.parent_rollup_block_hash = + // %base64(bundle.parent_rollup_block_hash()), + "incoming bundle does not match current block, ignoring" + ); + return Err(e); + } else { + self.auction_handles + .get_mut(&auction_id) + .ok_or_eyre("unable to get handle for the given auction")? + .try_send_bundle(bundle) + .wrap_err("failed to add bundle to auction")?; + } + } else { + info!( + "cannot forward the received bundle to an auction because no auction is currently \ + running; dropping the bundle" + ); + } + Ok(()) } pub(crate) async fn join_next(&mut self) -> Option<(Id, eyre::Result<()>)> { diff --git a/crates/astria-auctioneer/src/auctioneer/inner/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/running.rs index eef8e03dfa..cb095cd483 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/running.rs @@ -10,7 +10,6 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::StreamExt as _; -use telemetry::display::base64; use tokio::select; use tokio_util::sync::CancellationToken; use tracing::{ @@ -22,7 +21,6 @@ use tracing::{ use super::RunState; use crate::{ - auction, rollup_channel::{ BundleStream, ExecuteOptimisticBlockStream, @@ -37,7 +35,6 @@ pub(super) struct Running { pub(super) auctions: crate::auction::Manager, pub(super) block_commitments: BlockCommitmentStream, pub(super) bundles: BundleStream, - pub(super) current_block: crate::block::Current, pub(super) executed_blocks: ExecuteOptimisticBlockStream, pub(super) optimistic_blocks: OptimisticBlockStream, pub(super) rollup_id: RollupId, @@ -96,29 +93,17 @@ impl Running { Ok(()) } - #[instrument(skip(self), fields(auction.old_id = %base64(self.current_block.sequencer_block_hash())), err)] + // #[instrument(skip(self), fields(auction.old_id = + // %base64(self.current_block.sequencer_block_hash())), err)] fn handle_optimistic_block( &mut self, optimistic_block: eyre::Result, ) -> eyre::Result<()> { let optimistic_block = - optimistic_block.wrap_err("encountered problems receiving optimistic block message")?; + optimistic_block.wrap_err("encountered problem receiving optimistic block message")?; - let old_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .abort_auction(old_auction_id) - .wrap_err("failed to abort auction")?; - - info!( - optimistic_block.sequencer_block_hash = %base64(optimistic_block.block_hash()), - "received optimistic block, aborting old auction and starting new auction" - ); - - self.current_block = crate::block::Current::with_optimistic(optimistic_block.clone()); - let new_auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions.new_auction(new_auction_id); + // FIXME: Don't clone this; find a better way. + self.auctions.new_auction(optimistic_block.clone()); let base_block = crate::block::Optimistic::new(optimistic_block) .try_into_base_block(self.rollup_id) @@ -131,79 +116,41 @@ impl Running { Ok(()) } - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())), err)] + // #[instrument(skip_all, fields(auction.id = + // %base64(self.current_block.sequencer_block_hash())), err)] fn handle_block_commitment( &mut self, block_commitment: eyre::Result, ) -> eyre::Result<()> { let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; - if let Err(e) = self.current_block.commitment(block_commitment.clone()) { - warn!( - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - block_commitment.sequencer_block_hash = %base64(block_commitment.sequencer_block_hash()), - "received block commitment for the wrong block" - ); - return Err(e).wrap_err("failed to handle block commitment"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .start_timer(auction_id) + .start_timer(block_commitment) .wrap_err("failed to start timer")?; Ok(()) } - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + // #[instrument(skip_all, fields(auction.id = + // %base64(self.current_block.sequencer_block_hash())))] fn handle_executed_block( &mut self, executed_block: eyre::Result, ) -> eyre::Result<()> { let executed_block = executed_block.wrap_err("failed to receive executed block")?; - - if let Err(e) = self.current_block.execute(executed_block.clone()) { - warn!( - // TODO: nicer display for the current block - current_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - executed_block.sequencer_block_hash = %base64(executed_block.sequencer_block_hash()), - executed_block.rollup_block_hash = %base64(executed_block.rollup_block_hash()), - "received optimistic execution result for wrong sequencer block" - ); - return Err(e).wrap_err("failed to handle executed block"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); - self.auctions - .start_processing_bids(auction_id) + .start_processing_bids(executed_block) .wrap_err("failed to start processing bids")?; - Ok(()) } - #[instrument(skip_all, fields(auction.id = %base64(self.current_block.sequencer_block_hash())))] + // #[instrument(skip_all, fields(auction.id = + // %base64(self.current_block.sequencer_block_hash())))] fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { let bundle = bundle.wrap_err("received problematic bundle")?; - if let Err(e) = self.current_block.ensure_bundle_is_valid(&bundle) { - warn!( - curent_block.sequencer_block_hash = %base64(self.current_block.sequencer_block_hash()), - bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - bundle.parent_rollup_block_hash = %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - return Err(e).wrap_err("failed to handle bundle"); - } - - let auction_id = - auction::Id::from_sequencer_block_hash(self.current_block.sequencer_block_hash()); self.auctions - .try_send_bundle(auction_id, bundle) - .wrap_err("failed to submit bundle to auction")?; - + .forward_bundle_to_auction(bundle) + .wrap_err("failed to forward bundle to auction")?; Ok(()) } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs index 084da0d923..72ffd8ff56 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs @@ -3,10 +3,8 @@ use std::time::Duration; use astria_core::primitive::v1::RollupId; use astria_eyre::eyre::{ self, - OptionExt as _, WrapErr as _, }; -use futures::StreamExt as _; use tokio::select; use tokio_util::sync::CancellationToken; @@ -110,20 +108,16 @@ impl Starting { sequencer_channel, shutdown_token, } = self; - let (bundles, executed_blocks, block_commitments, (optimistic_blocks, current_block)) = tokio::try_join!( + let (bundles, executed_blocks, block_commitments, optimistic_blocks) = tokio::try_join!( open_bundle_stream(rollup_channel.clone()), open_execute_optimistic_block_stream(rollup_channel.clone()), open_block_commitment_stream(sequencer_channel.clone()), - open_optimistic_block_stream_and_get_current_block( - sequencer_channel.clone(), - rollup_id - ), + open_optimistic_block_stream(sequencer_channel.clone(), rollup_id), )?; Ok(Running { auctions, block_commitments, bundles, - current_block, executed_blocks, optimistic_blocks, rollup_id, @@ -133,28 +127,18 @@ impl Starting { } } -async fn open_optimistic_block_stream_and_get_current_block( +async fn open_optimistic_block_stream( chan: SequencerChannel, rollup_id: RollupId, -) -> eyre::Result<(OptimisticBlockStream, crate::block::Current)> { - let mut the_stream = chan - .open_get_optimistic_block_stream(rollup_id) +) -> eyre::Result { + chan.open_get_optimistic_block_stream(rollup_id) .await .wrap_err_with(|| { format!( "failed to open optimistic block stream to Sequencer node for rollup ID \ `{rollup_id}`" ) - })?; - let optimistic_block = the_stream - .next() - .await - .ok_or_eyre("optimistic block stream closed before yielding the current block")? - .wrap_err( - "failed to get current optimistic block after opening a stream to the Sequencer node", - )?; - let current_block = crate::block::Current::with_optimistic(optimistic_block); - Ok((the_stream, current_block)) + }) } async fn open_block_commitment_stream( diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index 79fbd76120..0b2b6ebe15 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -103,6 +103,8 @@ impl Optimistic { *self.filtered_sequencer_block.block_hash() } + // TODO: Actually consider removing this because the height seems superfluouos. + #[expect(dead_code, reason = "to quiet the warnings for now")] pub(crate) fn sequencer_height(&self) -> u64 { self.filtered_sequencer_block.height().into() } @@ -150,13 +152,14 @@ impl Executed { .expect("rollup block hash must be 32 bytes") } - pub(crate) fn rollup_block_hash(&self) -> [u8; 32] { - self.block - .hash() - .as_ref() - .try_into() - .expect("rollup block hash must be 32 bytes") - } + // TODO: consider removing this + // pub(crate) fn rollup_block_hash(&self) -> [u8; 32] { + // self.block + // .hash() + // .as_ref() + // .try_into() + // .expect("rollup block hash must be 32 bytes") + // } } #[derive(Debug, Clone)] @@ -187,6 +190,9 @@ impl Commitment { self.sequnecer_block_hash } + /// The height of the sequencer block that was committed. + // TODO: Actually consider removing this because the height seems superfluouos. + #[expect(dead_code, reason = "to quiet the warnings for now")] pub(crate) fn sequencer_height(&self) -> u64 { self.sequencer_height } @@ -213,28 +219,33 @@ impl Current { /// Updates the `Current` with the given `executed_block`. /// This will fail if the `executed_block` does not match the `optimistic_block`'s sequencer /// block hash. - pub(crate) fn execute(&mut self, executed_block: Executed) -> eyre::Result<()> { - if executed_block.sequencer_block_hash() != self.optimistic.sequencer_block_hash() { - return Err(eyre!("block hash mismatch")); + pub(crate) fn execute(&mut self, executed_block: Executed) -> bool { + let executed_matches_optimistic = + executed_block.sequencer_block_hash() != self.optimistic.sequencer_block_hash(); + if executed_matches_optimistic { + // TODO: What to do if we overwrote it (if we had already received an execute block + // with the same ID)? Emit a warning? Just overwrite? + let _ = self.executed.replace(executed_block); } - - self.executed = Some(executed_block); - Ok(()) + executed_matches_optimistic } - /// Updates the `Current` with the given `block_commitment`. - /// This will fail if the `block_commitment` does not match the `optimistic_block`'s sequencer - /// block hash. - pub(crate) fn commitment(&mut self, block_commitment: Commitment) -> eyre::Result<()> { - if block_commitment.sequencer_block_hash() != self.optimistic.sequencer_block_hash() { - return Err(eyre!("block hash mismatch")); - } - if block_commitment.sequencer_height() != self.optimistic.sequencer_height() { - return Err(eyre!("block height mismatch")); + /// Updates the currently tracked block with the provided `block_commitment` if + /// the contained sequencer block hash matches that of the tracked block. + /// + /// Returns if the the block was updated. + pub(crate) fn commitment(&mut self, block_commitment: Commitment) -> bool { + let hashes_match = + block_commitment.sequencer_block_hash() == self.optimistic.sequencer_block_hash(); + // TODO: Also checking the height seems excessive: just the block hash should be enough. + // if block_commitment.sequencer_height() != self.optimistic.sequencer_height() { + // return Err(eyre!("block height mismatch")); + // } + if hashes_match { + // TODO: What to do if the block commitment was previously received? + let _ = self.commitment.replace(block_commitment); } - - self.commitment = Some(block_commitment); - Ok(()) + hashes_match } pub(crate) fn sequencer_block_hash(&self) -> [u8; 32] { From 7a0fd7c9172a1bc0e97751158abec586a2c0c590 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 27 Nov 2024 15:51:02 +0100 Subject: [PATCH 35/73] replace the joinset of live auctions by a single auction task the running auctions were cancelled/aborted every time a new optimistic block was received. This means that no matter if the auction itself was not finished because a block replacement occured or because it could not be submitted to the mempool: they would always end up being cancelled. This means a single auction is all that's necessary because no more than one auction future at a time may be live. This was implemented using a new RunningAction type, that itself tracks its auction ID, the spawned task and a channel to it, and a parent block hash after having received an executed block. These changes mean that the CurrentBlock type could be removed. --- .../astria-auctioneer/src/auction/builder.rs | 11 - .../astria-auctioneer/src/auction/manager.rs | 194 +++++++++--------- crates/astria-auctioneer/src/auction/mod.rs | 24 +-- .../src/auctioneer/inner/running.rs | 30 +-- .../src/auctioneer/inner/starting.rs | 1 - crates/astria-auctioneer/src/block/mod.rs | 109 ---------- 6 files changed, 107 insertions(+), 262 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/builder.rs b/crates/astria-auctioneer/src/auction/builder.rs index 3bcb28adf2..c2e6167052 100644 --- a/crates/astria-auctioneer/src/auction/builder.rs +++ b/crates/astria-auctioneer/src/auction/builder.rs @@ -8,7 +8,6 @@ use astria_core::{ }, }; use tokio::sync::mpsc; -use tokio_util::sync::CancellationToken; use super::{ Auction, @@ -16,12 +15,7 @@ use super::{ Id, SequencerKey, }; -use crate::Metrics; - pub(crate) struct Builder { - pub(crate) metrics: &'static Metrics, - pub(crate) cancellation_token: CancellationToken, - /// The endpoint for the sequencer gRPC service used to get pending nonces pub(crate) sequencer_grpc_client: SequencerServiceClient, /// The endpoint for the sequencer ABCI service used to submit transactions @@ -44,8 +38,6 @@ pub(crate) struct Builder { impl Builder { pub(crate) fn build(self) -> (Handle, Auction) { let Self { - metrics, - cancellation_token, sequencer_grpc_client, sequencer_abci_client, latency_margin, @@ -61,8 +53,6 @@ impl Builder { let (new_bundles_tx, new_bundles_rx) = mpsc::channel(16); let auction = Auction { - metrics, - cancellation_token: cancellation_token.clone(), sequencer_grpc_client, sequencer_abci_client, commands_rx, @@ -77,7 +67,6 @@ impl Builder { ( Handle { - cancellation_token, commands_tx, new_bundles_tx, }, diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index 4634262e67..3a50061325 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -1,7 +1,5 @@ /// The auction Manager is responsible for managing running auction futures and their /// associated handles. -use std::collections::HashMap; - use astria_core::{ generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, primitive::v1::{ @@ -12,13 +10,9 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, - OptionExt as _, WrapErr as _, }; -use tokio_util::{ - sync::CancellationToken, - task::JoinMap, -}; +use tokio::task::JoinHandle; use tonic::transport::Channel; use tracing::{ info, @@ -28,8 +22,6 @@ use tracing::{ use super::{ Bundle, - Handle, - Id, SequencerKey, }; use crate::{ @@ -39,7 +31,6 @@ use crate::{ pub(crate) struct Builder { pub(crate) metrics: &'static crate::Metrics, - pub(crate) shutdown_token: CancellationToken, /// The gRPC endpoint for the sequencer service used by auctions. pub(crate) sequencer_grpc_client: SequencerServiceClient, @@ -63,7 +54,6 @@ impl Builder { pub(crate) fn build(self) -> eyre::Result { let Self { metrics, - shutdown_token, sequencer_grpc_client, sequencer_abci_endpoint, latency_margin, @@ -87,39 +77,44 @@ impl Builder { Ok(Manager { metrics, - cancellation_token: shutdown_token, sequencer_grpc_client, sequencer_abci_client, latency_margin, - running_auctions: JoinMap::new(), - auction_handles: HashMap::new(), + running_auction: None, sequencer_key, fee_asset_denomination, sequencer_chain_id, rollup_id, - - current_block: None, }) } } +struct RunningAuction { + id: crate::auction::Id, + height: u64, + parent_block_of_executed: Option<[u8; 32]>, + // TODO: Rename this to AuctionSender or smth like that + sender: crate::auction::Handle, + task: JoinHandle>, +} + +impl RunningAuction { + fn abort(&self) { + self.task.abort() + } +} + pub(crate) struct Manager { - metrics: &'static crate::Metrics, - cancellation_token: CancellationToken, sequencer_grpc_client: SequencerServiceClient, + #[allow(dead_code)] + metrics: &'static crate::Metrics, sequencer_abci_client: sequencer_client::HttpClient, latency_margin: std::time::Duration, - // FIXME: Having a joinmap here is actually weird: if a new block arrives - // the old auction should always be nuked. Either the optimistic block was - // replaced (proposed block rejected), or a new block is being built (auctioneer - // failed to submit the winning allocation in time). - running_auctions: JoinMap>, - auction_handles: HashMap, + running_auction: Option, sequencer_key: SequencerKey, fee_asset_denomination: asset::Denom, sequencer_chain_id: String, rollup_id: RollupId, - current_block: Option, } impl Manager { @@ -128,27 +123,27 @@ impl Manager { #[instrument(skip(self))] pub(crate) fn new_auction(&mut self, block: FilteredSequencerBlock) { let new_auction_id = crate::auction::Id::from_sequencer_block_hash(*block.block_hash()); + let height = block.height().into(); - if let Some(old_block) = self - .current_block - .replace(crate::block::Current::with_optimistic(block)) - { - // TODO: Track the ID in the "current block" (or get rid of it altogether?) - let old_auction_id = - crate::auction::Id::from_sequencer_block_hash(old_block.sequencer_block_hash()); + if let Some(running_auction) = self.running_auction.take() { + // NOTE: We just throw away the old auction after aborting it. Is there + // value in `.join`ing it after the abort except for ensuring that it + // did indeed abort? What if the running auction is not tracked inside + // the "auction manager", but the auction manager is turned into a simpler + // factory so that the running auction is running inside the auctioneer? + // Then the auctioneer would always have the previous/old auction and could + // decide what to do with it. That might make this a cleaner implementation. + let old_auction_id = running_auction.id; info!( %new_auction_id, %old_auction_id, "received optimistic block, aborting old auction and starting new auction" ); - // TODO: provide feedback if the auction didn't exist?; - let _ = self.abort_auction(old_auction_id); + running_auction.abort(); } let (handle, auction) = super::Builder { - metrics: self.metrics, - cancellation_token: self.cancellation_token.child_token(), sequencer_grpc_client: self.sequencer_grpc_client.clone(), sequencer_abci_client: self.sequencer_abci_client.clone(), latency_margin: self.latency_margin, @@ -160,33 +155,27 @@ impl Manager { } .build(); - // spawn and save handle - self.running_auctions.spawn(new_auction_id, auction.run()); - self.auction_handles.insert(new_auction_id, handle); - } - - fn abort_auction(&mut self, auction_id: Id) -> eyre::Result<()> { - let handle = self - .auction_handles - .get(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")?; - handle.cancel(); - Ok(()) + self.running_auction = Some(RunningAuction { + id: new_auction_id, + height, + parent_block_of_executed: None, + sender: handle, + task: tokio::task::spawn(auction.run()), + }); } #[instrument(skip(self))] // pub(crate) fn start_timer(&mut self, auction_id: Id) -> eyre::Result<()> { pub(crate) fn start_timer(&mut self, block_commitment: Commitment) -> eyre::Result<()> { - let auction_id = + let id_according_to_block = crate::auction::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); - if let Some(current_block) = &mut self.current_block { - if current_block.commitment(block_commitment) { - let handle = self - .auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")?; - handle + if let Some(auction) = &mut self.running_auction { + if auction.id == id_according_to_block + && block_commitment.sequencer_height() == auction.height + { + auction + .sender .start_timer() .wrap_err("failed to send command to start timer to auction")?; } else { @@ -198,9 +187,8 @@ impl Manager { // %base64(block_commitment.sequencer_block_hash()), "received // block commitment for the wrong block" ); info!( - "not starting the auction timer because the sequencer block hash of the \ - commitment hash did not match - not match that of the currently running auction" + "not starting the auction timer because sequencer block hash and height of \ + the commitment did not match that of the running auction", ); } } else { @@ -219,11 +207,20 @@ impl Manager { &mut self, block: crate::block::Executed, ) -> eyre::Result<()> { - let auction_id = + let id_according_to_block = crate::auction::Id::from_sequencer_block_hash(block.sequencer_block_hash()); - if let Some(current_block) = &mut self.current_block { - if !current_block.execute(block) { + if let Some(auction) = &mut self.running_auction { + if auction.id == id_according_to_block { + // TODO: What if it was already set? Overwrite? Replace? Drop? + let _ = auction + .parent_block_of_executed + .replace(block.parent_rollup_block_hash()); + auction + .sender + .start_processing_bids() + .wrap_err("failed to send command to start processing bids")?; + } else { // TODO: bring back the fields to track the dropped block and current block // warn!( // // TODO: nicer display for the current block @@ -239,15 +236,6 @@ impl Manager { executed block from the rollup with a sequencer block hash that does not \ match that of the currently running auction; dropping the executed block" ); - } else { - let handle = self - .auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")?; - - handle - .start_processing_bids() - .wrap_err("failed to send command to start processing bids")?; } } else { info!( @@ -259,13 +247,34 @@ impl Manager { } pub(crate) fn forward_bundle_to_auction(&mut self, bundle: Bundle) -> eyre::Result<()> { - let auction_id = + let id_according_to_bundle = crate::auction::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); - if let Some(current_block) = &mut self.current_block { - if let Err(e) = current_block - .ensure_bundle_is_valid(&bundle) - .wrap_err("failed to handle bundle") - { + if let Some(auction) = &mut self.running_auction { + // TODO: remember to check the parent rollup block hash, i.e.: + // + // if let Some(bundle.rollup_parent_block_hash) = + // current_auction/block.parent_rollup_block_hash() { ensure!( + // bundle.parent_rollup_block_hash() == rollup_parent_block_hash, + // "bundle's rollup parent block hash {bundle_hash} does not match current + // rollup \ parent block hash {current_hash}", + // bundle_hash = base64(bundle.parent_rollup_block_hash()), + // current_hash = base64(rollup_parent_block_hash) + // ); + // } + let Some(parent_block_of_executed) = auction.parent_block_of_executed else { + eyre::bail!( + "received a new bundle but the current auction has not yet + received an execute block from the rollup; dropping the bundle" + ); + }; + let ids_match = auction.id == id_according_to_bundle; + let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); + if ids_match && parent_blocks_match { + auction + .sender + .try_send_bundle(bundle) + .wrap_err("failed to add bundle to auction")?; + } else { warn!( // TODO: Add these fields back in. Is it even necessary to return the error? // Can't we just fire the event here? necessary? @@ -277,13 +286,7 @@ impl Manager { // %base64(bundle.parent_rollup_block_hash()), "incoming bundle does not match current block, ignoring" ); - return Err(e); - } else { - self.auction_handles - .get_mut(&auction_id) - .ok_or_eyre("unable to get handle for the given auction")? - .try_send_bundle(bundle) - .wrap_err("failed to add bundle to auction")?; + eyre::bail!("auction ID and ID according to bundle don't match"); } } else { info!( @@ -294,22 +297,15 @@ impl Manager { Ok(()) } - pub(crate) async fn join_next(&mut self) -> Option<(Id, eyre::Result<()>)> { - if let Some((auction_id, result)) = self.running_auctions.join_next().await { - // TODO: get rid of this expect somehow - self.auction_handles - .remove(&auction_id) - .expect("handle should always exist for running auction"); - - Some((auction_id, flatten_join_result(result))) - } else { - None - } + pub(crate) async fn next_winner(&mut self) -> Option> { + let auction = self.running_auction.as_mut()?; + Some(flatten_join_result((&mut auction.task).await)) } - pub(crate) fn abort_all(&mut self) -> usize { - let number_of_live_auctions = self.running_auctions.len(); - self.running_auctions.abort_all(); - number_of_live_auctions + pub(crate) fn abort(&mut self) { + // TODO: Do we need to wait for it to finish? + if let Some(auction) = self.running_auction.take() { + auction.abort() + } } } diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auction/mod.rs index c3c06c2ad1..3083ac4118 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auction/mod.rs @@ -68,7 +68,6 @@ use tokio::{ select, sync::mpsc, }; -use tokio_util::sync::CancellationToken; use tracing::{ debug, error, @@ -81,7 +80,6 @@ use tracing::{ use crate::{ bundle::Bundle, sequencer_key::SequencerKey, - Metrics, }; pub(crate) mod manager; @@ -115,16 +113,11 @@ enum Command { } pub(crate) struct Handle { - cancellation_token: CancellationToken, commands_tx: mpsc::Sender, new_bundles_tx: mpsc::Sender, } impl Handle { - pub(crate) fn cancel(&self) { - self.cancellation_token.cancel(); - } - pub(crate) fn start_processing_bids(&mut self) -> eyre::Result<()> { self.commands_tx .try_send(Command::StartProcessingBids) @@ -150,10 +143,6 @@ impl Handle { } pub(crate) struct Auction { - #[allow(dead_code)] - metrics: &'static Metrics, - cancellation_token: CancellationToken, - /// The sequencer's gRPC client, used for fetching pending nonces sequencer_grpc_client: SequencerServiceClient, /// The sequencer's ABCI client, used for submitting transactions @@ -189,8 +178,6 @@ impl Auction { select! { biased; - () = self.cancellation_token.cancelled() => break Err(eyre!("received shutdown signal")), - // get the auction winner when the timer expires _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { break Ok(allocation_rule.winner()); @@ -216,7 +203,7 @@ impl Auction { nonce_fetch = { let client = self.sequencer_grpc_client.clone(); let &address = self.sequencer_key.address(); - Some(tokio::task::spawn(async move { get_pending_nonce(client, address, self.metrics).await })) + Some(tokio::task::spawn(async move { get_pending_nonce(client, address).await })) }; } } @@ -270,10 +257,7 @@ impl Auction { let submission_result = select! { biased; - // TODO: should this be Ok(())? or Ok("received shutdown signal")? - () = self.cancellation_token.cancelled() => Err(eyre!("received shutdown signal during auction result submission")), - - result = submit_transaction(self.sequencer_abci_client.clone(), transaction, self.metrics) => { + result = submit_transaction(self.sequencer_abci_client.clone(), transaction) => { // TODO: how to handle submission failure better? match result { Ok(resp) => { @@ -296,8 +280,6 @@ impl Auction { async fn get_pending_nonce( client: SequencerServiceClient, address: Address, - // TODO: emit metrics here - #[allow(unused_variables)] metrics: &'static Metrics, ) -> eyre::Result { let span = tracing::Span::current(); let retry_cfg = tryhard::RetryFutureConfig::new(1024) @@ -343,8 +325,6 @@ async fn get_pending_nonce( async fn submit_transaction( client: sequencer_client::HttpClient, transaction: Transaction, - // TODO: emit metrics here - #[allow(unused_variables)] metrics: &'static Metrics, ) -> eyre::Result { let span = tracing::Span::current(); let retry_cfg = tryhard::RetryFutureConfig::new(1024) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/running.rs index cb095cd483..ca23a34da0 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/running.rs @@ -16,7 +16,6 @@ use tracing::{ error, info, instrument, - warn, }; use super::RunState; @@ -67,10 +66,6 @@ impl Running { async fn handle_event(&mut self) -> eyre::Result<()> { select!( - Some((id, res)) = self.auctions.join_next() => { - res.wrap_err_with(|| format!("auction failed for block `{id}`"))?; - }, - res = self.optimistic_blocks.next() => { let res = res.ok_or_eyre("optimistic block stream closed")?; let _ = self.handle_optimistic_block(res); @@ -86,6 +81,10 @@ impl Running { let _ = self.handle_executed_block(res); } + Some(res) = self.auctions.next_winner() => { + let _ = self.handle_auction_winner(res); + } + Some(res) = self.bundles.next() => { let _ = self.handle_bundle(res); } @@ -93,6 +92,11 @@ impl Running { Ok(()) } + #[instrument(skip_all, err)] + fn handle_auction_winner(&self, res: eyre::Result<()>) -> eyre::Result<()> { + res + } + // #[instrument(skip(self), fields(auction.old_id = // %base64(self.current_block.sequencer_block_hash())), err)] fn handle_optimistic_block( @@ -170,21 +174,7 @@ impl Running { Ok(reason) => info!(%reason, message), Err(reason) => error!(%reason, message), }; - let shutdown_auctions = async { - while let Some((id, res)) = self.auctions.join_next().await { - if let Err(error) = res { - // FIXME: probide a display impl for this ID - warn!(?id, %error, "auction ended with an error"); - } - } - }; - // NOTE: we don't care if this elapses. We will abort all auctions anyway - // and report if there were any still running. - let _ = tokio::time::timeout(WAIT_BEFORE_ABORT, shutdown_auctions).await; - let aborted = self.auctions.abort_all(); - if aborted > 0 { - warn!("aborted `{aborted}` auctions still running after grace period",); - } + self.auctions.abort(); reason.map(|_| RunState::Cancelled) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs index 72ffd8ff56..7566a2de84 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs @@ -69,7 +69,6 @@ impl Starting { // TODO: Rearchitect this thing let auctions = crate::auction::manager::Builder { metrics, - shutdown_token: shutdown_token.clone(), sequencer_grpc_client: sequencer_channel.to_sequencer_service_client(), sequencer_abci_endpoint, latency_margin: Duration::from_millis(latency_margin_ms), diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index 0b2b6ebe15..0cb79a8fd8 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -21,15 +21,11 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, - ensure, eyre, Context, }; use bytes::Bytes; use prost::Message as _; -use telemetry::display::base64; - -use crate::bundle::Bundle; /// Converts a [`tendermint::Time`] to a [`prost_types::Timestamp`]. fn convert_tendermint_time_to_protobuf_timestamp( @@ -98,16 +94,6 @@ impl Optimistic { timestamp, }) } - - pub(crate) fn sequencer_block_hash(&self) -> [u8; 32] { - *self.filtered_sequencer_block.block_hash() - } - - // TODO: Actually consider removing this because the height seems superfluouos. - #[expect(dead_code, reason = "to quiet the warnings for now")] - pub(crate) fn sequencer_height(&self) -> u64 { - self.filtered_sequencer_block.height().into() - } } #[derive(Debug, Clone)] @@ -151,15 +137,6 @@ impl Executed { .try_into() .expect("rollup block hash must be 32 bytes") } - - // TODO: consider removing this - // pub(crate) fn rollup_block_hash(&self) -> [u8; 32] { - // self.block - // .hash() - // .as_ref() - // .try_into() - // .expect("rollup block hash must be 32 bytes") - // } } #[derive(Debug, Clone)] @@ -191,93 +168,7 @@ impl Commitment { } /// The height of the sequencer block that was committed. - // TODO: Actually consider removing this because the height seems superfluouos. - #[expect(dead_code, reason = "to quiet the warnings for now")] pub(crate) fn sequencer_height(&self) -> u64 { self.sequencer_height } } - -pub(crate) struct Current { - optimistic: Optimistic, - executed: Option, - commitment: Option, -} - -impl Current { - /// Creates a new `Current` with the given `optimistic_block`. - pub(crate) fn with_optimistic(filtered_sequencer_block: FilteredSequencerBlock) -> Self { - Self { - optimistic: Optimistic { - filtered_sequencer_block, - }, - executed: None, - commitment: None, - } - } - - /// Updates the `Current` with the given `executed_block`. - /// This will fail if the `executed_block` does not match the `optimistic_block`'s sequencer - /// block hash. - pub(crate) fn execute(&mut self, executed_block: Executed) -> bool { - let executed_matches_optimistic = - executed_block.sequencer_block_hash() != self.optimistic.sequencer_block_hash(); - if executed_matches_optimistic { - // TODO: What to do if we overwrote it (if we had already received an execute block - // with the same ID)? Emit a warning? Just overwrite? - let _ = self.executed.replace(executed_block); - } - executed_matches_optimistic - } - - /// Updates the currently tracked block with the provided `block_commitment` if - /// the contained sequencer block hash matches that of the tracked block. - /// - /// Returns if the the block was updated. - pub(crate) fn commitment(&mut self, block_commitment: Commitment) -> bool { - let hashes_match = - block_commitment.sequencer_block_hash() == self.optimistic.sequencer_block_hash(); - // TODO: Also checking the height seems excessive: just the block hash should be enough. - // if block_commitment.sequencer_height() != self.optimistic.sequencer_height() { - // return Err(eyre!("block height mismatch")); - // } - if hashes_match { - // TODO: What to do if the block commitment was previously received? - let _ = self.commitment.replace(block_commitment); - } - hashes_match - } - - pub(crate) fn sequencer_block_hash(&self) -> [u8; 32] { - self.optimistic.sequencer_block_hash() - } - - pub(crate) fn parent_rollup_block_hash(&self) -> Option<[u8; 32]> { - self.executed - .as_ref() - .map(Executed::parent_rollup_block_hash) - } - - /// Ensures that the given `bundle` is valid for the current block state. - pub(crate) fn ensure_bundle_is_valid(&self, bundle: &Bundle) -> eyre::Result<()> { - ensure!( - bundle.base_sequencer_block_hash() == self.sequencer_block_hash(), - "incoming bundle's sequencer block hash {bundle_hash} does not match current \ - sequencer block hash {current_hash}", - bundle_hash = base64(bundle.base_sequencer_block_hash()), - current_hash = base64(self.sequencer_block_hash()) - ); - - if let Some(rollup_parent_block_hash) = self.parent_rollup_block_hash() { - ensure!( - bundle.parent_rollup_block_hash() == rollup_parent_block_hash, - "bundle's rollup parent block hash {bundle_hash} does not match current rollup \ - parent block hash {current_hash}", - bundle_hash = base64(bundle.parent_rollup_block_hash()), - current_hash = base64(rollup_parent_block_hash) - ); - } - - Ok(()) - } -} From 66b7f2db0a2ff498e453047d51125adcf5665748 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 27 Nov 2024 16:57:48 +0100 Subject: [PATCH 36/73] submit auction winners without a retry assuming sequencer works normally, there is no point even retrying to submit the auction winner: by the time we submit, get the response, and then try again we are likely out of time and sequenucer has started a new block. --- crates/astria-auctioneer/src/auction/mod.rs | 81 ++++++--------------- 1 file changed, 23 insertions(+), 58 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auction/mod.rs index 3083ac4118..b7eb572fc3 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auction/mod.rs @@ -50,7 +50,6 @@ use astria_core::{ asset, RollupId, }, - protocol::transaction::v1::Transaction, }; use astria_eyre::eyre::{ self, @@ -60,7 +59,6 @@ use astria_eyre::eyre::{ }; pub(crate) use builder::Builder; use sequencer_client::{ - tendermint_rpc::endpoint::broadcast::tx_sync, Address, SequencerClientExt, }; @@ -166,6 +164,7 @@ pub(crate) struct Auction { } impl Auction { + #[instrument(skip_all, fields(id = %self.id))] pub(crate) async fn run(mut self) -> eyre::Result<()> { let mut latency_margin_timer = None; // TODO: do we want to make this configurable to allow for more complex allocation rules? @@ -254,25 +253,29 @@ impl Auction { let transaction = transaction_body.sign(self.sequencer_key.signing_key()); - let submission_result = select! { - biased; - - result = submit_transaction(self.sequencer_abci_client.clone(), transaction) => { - // TODO: how to handle submission failure better? - match result { - Ok(resp) => { - // TODO: handle failed submission instead of just logging the result - info!(auction.id = %self.id, auction.result = %resp.log, "auction result submitted to sequencer"); - Ok(()) - }, - Err(e) => { - error!(auction.id = %self.id, err = %e, "failed to submit auction result to sequencer"); - Err(e).wrap_err("failed to submit auction result to sequencer") - }, - } + // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, + // it's likey lost. + // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once + // if the response didn't take too long? But it's probably a bad idea to even try. + match self + .sequencer_abci_client + .submit_transaction_sync(transaction) + .await + .wrap_err("submission of the auction failed; it's likely lost") + { + Ok(resp) => { + // TODO: provide tx_sync response hash? Does it have extra meaning? + info!( + response.log = %resp.log, + response.code = resp.code.value(), + "auction winner submitted to sequencer", + ); } - }; - submission_result + Err(error) => { + error!(%error, "failed to submit auction winner to sequencer; it's likely lost"); + } + } + Ok(()) } } @@ -321,41 +324,3 @@ async fn get_pending_nonce( Ok(nonce) } - -async fn submit_transaction( - client: sequencer_client::HttpClient, - transaction: Transaction, -) -> eyre::Result { - let span = tracing::Span::current(); - let retry_cfg = tryhard::RetryFutureConfig::new(1024) - .exponential_backoff(Duration::from_millis(100)) - .max_delay(Duration::from_secs(2)) - .on_retry( - move |attempt: u32, - next_delay: Option, - error: &sequencer_client::extension_trait::Error| { - let wait_duration = next_delay - .map(humantime::format_duration) - .map(tracing::field::display); - warn!( - parent: &span, - attempt, - wait_duration, - error = error as &dyn std::error::Error, - "attempt to submit transaction failed; retrying after backoff", - ); - futures::future::ready(()) - }, - ); - - tryhard::retry_fn(|| { - let client = client.clone(); - let transaction = transaction.clone(); - - async move { client.submit_transaction_sync(transaction).await } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to submit transaction") -} From 17bb3e77d3aea0eebb7aee39301a03ace6e30691 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Thu, 28 Nov 2024 01:34:02 +0100 Subject: [PATCH 37/73] fetch pending nonces on a schedule and uncouple from auction winner submission --- .../astria-auctioneer/src/auction/builder.rs | 17 ++- .../astria-auctioneer/src/auction/manager.rs | 28 ++--- crates/astria-auctioneer/src/auction/mod.rs | 97 ++------------- .../src/auctioneer/inner/mod.rs | 2 + .../src/auctioneer/inner/running.rs | 111 +++++++++++++++++- .../src/auctioneer/inner/starting.rs | 22 +++- .../astria-auctioneer/src/auctioneer/mod.rs | 1 + .../src/sequencer_channel.rs | 44 +++++-- 8 files changed, 187 insertions(+), 135 deletions(-) diff --git a/crates/astria-auctioneer/src/auction/builder.rs b/crates/astria-auctioneer/src/auction/builder.rs index c2e6167052..abc1e59f44 100644 --- a/crates/astria-auctioneer/src/auction/builder.rs +++ b/crates/astria-auctioneer/src/auction/builder.rs @@ -1,11 +1,8 @@ use std::time::Duration; -use astria_core::{ - generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, - primitive::v1::{ - asset, - RollupId, - }, +use astria_core::primitive::v1::{ + asset, + RollupId, }; use tokio::sync::mpsc; @@ -15,9 +12,8 @@ use super::{ Id, SequencerKey, }; +use crate::auctioneer::PendingNonceSubscriber; pub(crate) struct Builder { - /// The endpoint for the sequencer gRPC service used to get pending nonces - pub(crate) sequencer_grpc_client: SequencerServiceClient, /// The endpoint for the sequencer ABCI service used to submit transactions pub(crate) sequencer_abci_client: sequencer_client::HttpClient, /// The amount of time to wait after a commit before closing the auction for bids and @@ -33,12 +29,12 @@ pub(crate) struct Builder { pub(crate) sequencer_chain_id: String, /// The rollup ID used for `RollupDataSubmission` with the auction result pub(crate) rollup_id: RollupId, + pub(crate) pending_nonce: PendingNonceSubscriber, } impl Builder { pub(crate) fn build(self) -> (Handle, Auction) { let Self { - sequencer_grpc_client, sequencer_abci_client, latency_margin, auction_id, @@ -46,6 +42,7 @@ impl Builder { rollup_id, sequencer_key, sequencer_chain_id, + pending_nonce, } = self; // TODO: get the capacities from config or something instead of using a magic number @@ -53,7 +50,6 @@ impl Builder { let (new_bundles_tx, new_bundles_rx) = mpsc::channel(16); let auction = Auction { - sequencer_grpc_client, sequencer_abci_client, commands_rx, new_bundles_rx, @@ -63,6 +59,7 @@ impl Builder { fee_asset_denomination, sequencer_chain_id, rollup_id, + pending_nonce, }; ( diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auction/manager.rs index 3a50061325..4100d3cac1 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auction/manager.rs @@ -1,7 +1,6 @@ /// The auction Manager is responsible for managing running auction futures and their /// associated handles. use astria_core::{ - generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient, primitive::v1::{ asset, RollupId, @@ -13,7 +12,6 @@ use astria_eyre::eyre::{ WrapErr as _, }; use tokio::task::JoinHandle; -use tonic::transport::Channel; use tracing::{ info, instrument, @@ -25,6 +23,7 @@ use super::{ SequencerKey, }; use crate::{ + auctioneer::PendingNonceSubscriber, block::Commitment, flatten_join_result, }; @@ -32,52 +31,40 @@ use crate::{ pub(crate) struct Builder { pub(crate) metrics: &'static crate::Metrics, - /// The gRPC endpoint for the sequencer service used by auctions. - pub(crate) sequencer_grpc_client: SequencerServiceClient, /// The ABCI endpoint for the sequencer service used by auctions. pub(crate) sequencer_abci_endpoint: String, /// The amount of time to run the auction timer for. pub(crate) latency_margin: std::time::Duration, /// The private key used to sign sequencer transactions. - pub(crate) sequencer_private_key_path: String, - /// The prefix for the address used to sign sequencer transactions - pub(crate) sequencer_address_prefix: String, + pub(crate) sequencer_key: SequencerKey, /// The denomination of the fee asset used in the sequencer transactions pub(crate) fee_asset_denomination: asset::Denom, /// The chain ID for sequencer transactions pub(crate) sequencer_chain_id: String, /// The rollup ID for the `RollupDataSubmission`s with auction results pub(crate) rollup_id: RollupId, + pub(crate) pending_nonce: PendingNonceSubscriber, } impl Builder { pub(crate) fn build(self) -> eyre::Result { let Self { metrics, - sequencer_grpc_client, sequencer_abci_endpoint, latency_margin, fee_asset_denomination, rollup_id, - sequencer_private_key_path, - sequencer_address_prefix, + sequencer_key, sequencer_chain_id, + pending_nonce, } = self; - let sequencer_key = SequencerKey::builder() - .path(sequencer_private_key_path) - .prefix(sequencer_address_prefix) - .try_build() - .wrap_err("failed to load sequencer private key")?; - info!(address = %sequencer_key.address(), "loaded sequencer signer"); - let sequencer_abci_client = sequencer_client::HttpClient::new(sequencer_abci_endpoint.as_str()) .wrap_err("failed constructing sequencer abci client")?; Ok(Manager { metrics, - sequencer_grpc_client, sequencer_abci_client, latency_margin, running_auction: None, @@ -85,6 +72,7 @@ impl Builder { fee_asset_denomination, sequencer_chain_id, rollup_id, + pending_nonce, }) } } @@ -105,7 +93,6 @@ impl RunningAuction { } pub(crate) struct Manager { - sequencer_grpc_client: SequencerServiceClient, #[allow(dead_code)] metrics: &'static crate::Metrics, sequencer_abci_client: sequencer_client::HttpClient, @@ -115,6 +102,7 @@ pub(crate) struct Manager { fee_asset_denomination: asset::Denom, sequencer_chain_id: String, rollup_id: RollupId, + pending_nonce: PendingNonceSubscriber, } impl Manager { @@ -144,7 +132,6 @@ impl Manager { } let (handle, auction) = super::Builder { - sequencer_grpc_client: self.sequencer_grpc_client.clone(), sequencer_abci_client: self.sequencer_abci_client.clone(), latency_margin: self.latency_margin, auction_id: new_auction_id, @@ -152,6 +139,7 @@ impl Manager { fee_asset_denomination: self.fee_asset_denomination.clone(), sequencer_chain_id: self.sequencer_chain_id.clone(), rollup_id: self.rollup_id, + pending_nonce: self.pending_nonce.clone(), } .build(); diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auction/mod.rs index b7eb572fc3..0992ad1fbf 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auction/mod.rs @@ -41,15 +41,9 @@ mod builder; use std::time::Duration; use allocation_rule::FirstPrice; -use astria_core::{ - generated::sequencerblock::v1::{ - sequencer_service_client::SequencerServiceClient, - GetPendingNonceRequest, - }, - primitive::v1::{ - asset, - RollupId, - }, +use astria_core::primitive::v1::{ + asset, + RollupId, }; use astria_eyre::eyre::{ self, @@ -58,10 +52,7 @@ use astria_eyre::eyre::{ OptionExt as _, }; pub(crate) use builder::Builder; -use sequencer_client::{ - Address, - SequencerClientExt, -}; +use sequencer_client::SequencerClientExt; use tokio::{ select, sync::mpsc, @@ -71,11 +62,10 @@ use tracing::{ error, info, instrument, - warn, - Instrument, }; use crate::{ + auctioneer::PendingNonceSubscriber, bundle::Bundle, sequencer_key::SequencerKey, }; @@ -141,8 +131,6 @@ impl Handle { } pub(crate) struct Auction { - /// The sequencer's gRPC client, used for fetching pending nonces - sequencer_grpc_client: SequencerServiceClient, /// The sequencer's ABCI client, used for submitting transactions sequencer_abci_client: sequencer_client::HttpClient, /// Channel for receiving commands sent via the handle @@ -161,6 +149,7 @@ pub(crate) struct Auction { sequencer_chain_id: String, /// Rollup ID to submit the auction result to rollup_id: RollupId, + pending_nonce: PendingNonceSubscriber, } impl Auction { @@ -171,8 +160,6 @@ impl Auction { let mut allocation_rule = FirstPrice::new(); let mut auction_is_open = false; - let mut nonce_fetch: Option>> = None; - let auction_result = loop { select! { biased; @@ -197,13 +184,6 @@ impl Auction { // set the timer latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); - - // we wait for commit because we want the pending nonce from the committed block - nonce_fetch = { - let client = self.sequencer_grpc_client.clone(); - let &address = self.sequencer_key.address(); - Some(tokio::task::spawn(async move { get_pending_nonce(client, address).await })) - }; } } } @@ -227,24 +207,12 @@ impl Auction { } }; - // TODO: separate the rest of this to a different object, e.g. AuctionResult? - // TODO: flatten this or get rid of the option somehow? - // await the nonce fetch result - let nonce = nonce_fetch - .expect( - "should have received commit and fetched pending nonce before exiting the auction \ - loop", - ) - .await - .wrap_err("get_pending_nonce task failed")? - .wrap_err("failed to fetch nonce")?; - - // serialize, sign and submit to the sequencer + // TODO: report the pending nonce that we ended up using. let transaction_body = auction_result .wrap_err("auction failed unexpectedly")? .ok_or_eyre("auction ended with no winning bid")? .into_transaction_body( - nonce, + self.pending_nonce.get(), self.rollup_id, self.sequencer_key.clone(), self.fee_asset_denomination.clone(), @@ -257,6 +225,9 @@ impl Auction { // it's likey lost. // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once // if the response didn't take too long? But it's probably a bad idea to even try. + // Can we detect if a submission failed due to a bad nonce? In this case, we could + // immediately ("optimistically") submit with the most recent pending nonce (if the + // publisher updated it in the meantime) or just nonce + 1 (if it didn't yet update)? match self .sequencer_abci_client .submit_transaction_sync(transaction) @@ -278,49 +249,3 @@ impl Auction { Ok(()) } } - -#[instrument(skip_all, fields(%address, err))] -async fn get_pending_nonce( - client: SequencerServiceClient, - address: Address, -) -> eyre::Result { - let span = tracing::Span::current(); - let retry_cfg = tryhard::RetryFutureConfig::new(1024) - .exponential_backoff(Duration::from_millis(100)) - .max_delay(Duration::from_secs(2)) - .on_retry( - move |attempt: u32, next_delay: Option, error: &tonic::Status| { - let wait_duration = next_delay - .map(humantime::format_duration) - .map(tracing::field::display); - warn!( - parent: &span, - attempt, - wait_duration, - error = error as &dyn std::error::Error, - "attempt to get pending nonce failed; retrying after backoff", - ); - futures::future::ready(()) - }, - ); - - let nonce = tryhard::retry_fn(|| { - let mut client = client.clone(); - - async move { - client - .get_pending_nonce(GetPendingNonceRequest { - address: Some(address.into_raw()), - }) - .await - } - }) - .with_config(retry_cfg) - .in_current_span() - .await - .wrap_err("failed to get pending nonce")? - .into_inner() - .inner; - - Ok(nonce) -} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index d705a04129..29bf7fda70 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -12,6 +12,8 @@ use crate::{ mod running; mod starting; +// TODO: Don't export this all the way out of this module +pub(crate) use running::PendingNonceSubscriber; use running::Running; use starting::Starting; diff --git a/crates/astria-auctioneer/src/auctioneer/inner/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/running.rs index ca23a34da0..0db5980780 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/running.rs @@ -1,21 +1,36 @@ -use std::time::Duration; +use std::{ + self, + time::Duration, +}; use astria_core::{ - primitive::v1::RollupId, + primitive::v1::{ + Address, + RollupId, + }, sequencerblock::v1::block::FilteredSequencerBlock, }; use astria_eyre::eyre::{ self, + bail, OptionExt as _, WrapErr as _, }; -use futures::StreamExt as _; -use tokio::select; +use futures::{ + Future, + StreamExt as _, +}; +use tokio::{ + select, + task::JoinHandle, +}; use tokio_util::sync::CancellationToken; use tracing::{ error, info, + info_span, instrument, + warn, }; use super::RunState; @@ -27,15 +42,96 @@ use crate::{ sequencer_channel::{ BlockCommitmentStream, OptimisticBlockStream, + SequencerChannel, }, }; +#[derive(Clone, Debug)] +pub(crate) struct PendingNonceSubscriber { + inner: tokio::sync::watch::Receiver, +} + +impl PendingNonceSubscriber { + pub(crate) fn get(&self) -> u32 { + *self.inner.borrow() + } +} + +/// Fetches the latest pending nonce for a given address every 500ms. +// TODO: should this provide some kind of feedback mechanism from the +// auction submission? Automatic incrementing for example? Notificatoin +// that the nonce was actually bad? +pub(crate) struct PendingNoncePublisher { + sender: tokio::sync::watch::Sender, + task: JoinHandle<()>, +} + +impl PendingNoncePublisher { + pub(crate) fn subscribe(&self) -> PendingNonceSubscriber { + PendingNonceSubscriber { + inner: self.sender.subscribe(), + } + } + + pub(crate) fn new(channel: SequencerChannel, address: Address) -> Self { + use tokio::time::{ + timeout_at, + MissedTickBehavior, + }; + // TODO: make this configurable. Right now they assume a Sequencer block time of 2s, + // so this is fetching nonce up to 4 times a block. + const FETCH_INTERVAL: Duration = Duration::from_millis(500); + const FETCH_TIMEOUT: Duration = FETCH_INTERVAL.saturating_mul(2); + let (tx, _) = tokio::sync::watch::channel(0); + Self { + sender: tx.clone(), + task: tokio::task::spawn(async move { + let mut interval = tokio::time::interval(FETCH_INTERVAL); + interval.set_missed_tick_behavior(MissedTickBehavior::Delay); + let mut fetch = None; + loop { + select!( + instant = interval.tick(), if fetch.is_none() => { + fetch = Some(Box::pin( + timeout_at(instant + FETCH_TIMEOUT, channel.get_pending_nonce(address)))); + } + res = async { fetch.as_mut().unwrap().await }, if fetch.is_some() => { + fetch.take(); + let span = info_span!("fetch pending nonce"); + match res.map_err(eyre::Report::new) { + Ok(Ok(nonce)) => { + info!(nonce, %address, "received new pending from sequencer"); + tx.send_replace(nonce); + } + Ok(Err(error)) | Err(error) => span.in_scope(|| warn!(%error, "failed fetching pending nonce")), + } + } + ) + } + }), + } + } +} + +impl Future for PendingNoncePublisher { + type Output = Result<(), tokio::task::JoinError>; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + use futures::FutureExt as _; + self.task.poll_unpin(cx) + } +} + pub(super) struct Running { pub(super) auctions: crate::auction::Manager, pub(super) block_commitments: BlockCommitmentStream, pub(super) bundles: BundleStream, pub(super) executed_blocks: ExecuteOptimisticBlockStream, pub(super) optimistic_blocks: OptimisticBlockStream, + pub(super) pending_nonce: PendingNoncePublisher, pub(super) rollup_id: RollupId, pub(super) shutdown_token: CancellationToken, } @@ -88,6 +184,13 @@ impl Running { Some(res) = self.bundles.next() => { let _ = self.handle_bundle(res); } + + res = &mut self.pending_nonce => { + match res { + Ok(()) => bail!("endless pending nonce publisher task exicted unexpectedly"), + Err(err) => return Err(err).wrap_err("pending nonce publisher task panicked"), + } + } ); Ok(()) } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs index 7566a2de84..61fa299ea7 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs @@ -7,12 +7,14 @@ use astria_eyre::eyre::{ }; use tokio::select; use tokio_util::sync::CancellationToken; +use tracing::info; use super::{ RunState, Running, }; use crate::{ + auctioneer::inner::running::PendingNoncePublisher, rollup_channel::{ BundleStream, ExecuteOptimisticBlockStream, @@ -23,6 +25,7 @@ use crate::{ OptimisticBlockStream, SequencerChannel, }, + sequencer_key::SequencerKey, Config, Metrics, }; @@ -37,6 +40,7 @@ pub(super) fn run_state( pub(super) struct Starting { auctions: crate::auction::Manager, + pending_nonce: PendingNoncePublisher, rollup_channel: RollupChannel, rollup_id: RollupId, sequencer_channel: SequencerChannel, @@ -66,23 +70,33 @@ impl Starting { let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; + let sequencer_key = SequencerKey::builder() + .path(sequencer_private_key_path) + .prefix(sequencer_address_prefix) + .try_build() + .wrap_err("failed to load sequencer private key")?; + info!(address = %sequencer_key.address(), "loaded sequencer signer"); + + let pending_nonce = + PendingNoncePublisher::new(sequencer_channel.clone(), *sequencer_key.address()); + // TODO: Rearchitect this thing let auctions = crate::auction::manager::Builder { metrics, - sequencer_grpc_client: sequencer_channel.to_sequencer_service_client(), sequencer_abci_endpoint, latency_margin: Duration::from_millis(latency_margin_ms), - sequencer_private_key_path, - sequencer_address_prefix, + sequencer_key: sequencer_key.clone(), fee_asset_denomination, sequencer_chain_id, rollup_id, + pending_nonce: pending_nonce.subscribe(), } .build() .wrap_err("failed to initialize auction manager")?; Ok(Starting { auctions, + pending_nonce, rollup_channel, rollup_id, sequencer_channel, @@ -102,6 +116,7 @@ impl Starting { async fn start_running(self) -> eyre::Result { let Self { auctions, + pending_nonce, rollup_channel, rollup_id, sequencer_channel, @@ -121,6 +136,7 @@ impl Starting { optimistic_blocks, rollup_id, shutdown_token, + pending_nonce, } .into()) } diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index c803a205bf..1170f0fcba 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -18,6 +18,7 @@ use crate::{ }; mod inner; +pub(crate) use inner::PendingNonceSubscriber; /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. pub struct Auctioneer { diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 9fc3b476ff..f759bf0c18 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -4,16 +4,16 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::{ - optimisticblock::v1alpha1::{ - GetBlockCommitmentStreamRequest, - GetBlockCommitmentStreamResponse, - GetOptimisticBlockStreamRequest, - GetOptimisticBlockStreamResponse, - }, - v1::sequencer_service_client::SequencerServiceClient, + generated::sequencerblock::optimisticblock::v1alpha1::{ + GetBlockCommitmentStreamRequest, + GetBlockCommitmentStreamResponse, + GetOptimisticBlockStreamRequest, + GetOptimisticBlockStreamResponse, + }, + primitive::v1::{ + Address, + RollupId, }, - primitive::v1::RollupId, sequencerblock::v1::block::FilteredSequencerBlock, Protobuf as _, }; @@ -23,6 +23,7 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::{ + Future, Stream, StreamExt as _, }; @@ -32,7 +33,7 @@ use tonic::transport::Channel; use crate::block::Commitment; pub(crate) fn open(endpoint: &str) -> eyre::Result { - SequencerChannel::create(&endpoint) + SequencerChannel::create(endpoint) .wrap_err_with(|| format!("failed to create a gRPC channel to Sequencer at `{endpoint}`")) } @@ -54,8 +55,27 @@ impl SequencerChannel { }) } - pub(crate) fn to_sequencer_service_client(&self) -> SequencerServiceClient { - SequencerServiceClient::new(self.inner.clone()) + pub(crate) fn get_pending_nonce( + &self, + address: Address, + ) -> impl Future> { + use astria_core::generated::sequencerblock::v1::{ + sequencer_service_client::SequencerServiceClient, + GetPendingNonceRequest, + }; + + let mut client = SequencerServiceClient::new(self.inner.clone()); + async move { + let nonce = client + .get_pending_nonce(GetPendingNonceRequest { + address: Some(address.into_raw()), + }) + .await + .wrap_err("failed to fetch most recent pending nonce")? + .into_inner() + .inner; + Ok(nonce) + } } pub(crate) async fn open_get_block_commitment_stream( From 951f61059b10fa31ff8f4c42b58b7d100d1cb740 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 00:48:29 +0100 Subject: [PATCH 38/73] include a restart mechanism for all streams after they have concluded right now auctioneer just shuts down after a the connection to the rollup and/or sequencer dropped. dropping connection is normal and exepcted behavior, so it must have a restart mechanism for the full stream. --- Cargo.lock | 6 +- crates/astria-auctioneer/Cargo.toml | 3 +- .../src/auctioneer/inner/starting.rs | 61 +------ crates/astria-auctioneer/src/lib.rs | 1 + .../astria-auctioneer/src/rollup_channel.rs | 159 ++++++++++++------ .../src/sequencer_channel.rs | 133 +++++++++------ .../astria-auctioneer/src/streaming_utils.rs | 98 +++++++++++ 7 files changed, 307 insertions(+), 154 deletions(-) create mode 100644 crates/astria-auctioneer/src/streaming_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 94bd5a6cbb..8658964d7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,7 @@ dependencies = [ "humantime", "itertools 0.12.1", "pbjson-types", + "pin-project-lite", "prost", "serde", "serde_json", @@ -6101,9 +6102,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -8012,6 +8013,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util 0.7.12", ] [[package]] diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 4f2342baa8..9a64b531c2 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -38,8 +38,9 @@ tokio-util = { workspace = true, features = ["rt"] } tracing = { workspace = true, features = ["attributes"] } tryhard = { workspace = true } tonic = { workspace = true } -tokio-stream = { workspace = true, features = ["net"] } +tokio-stream = { workspace = true, features = ["sync"] } base64.workspace = true +pin-project-lite = "0.2.15" [dev-dependencies] config = { package = "astria-config", path = "../astria-config", features = [ diff --git a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs index 61fa299ea7..0882070097 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs @@ -15,16 +15,8 @@ use super::{ }; use crate::{ auctioneer::inner::running::PendingNoncePublisher, - rollup_channel::{ - BundleStream, - ExecuteOptimisticBlockStream, - RollupChannel, - }, - sequencer_channel::{ - BlockCommitmentStream, - OptimisticBlockStream, - SequencerChannel, - }, + rollup_channel::RollupChannel, + sequencer_channel::SequencerChannel, sequencer_key::SequencerKey, Config, Metrics, @@ -122,18 +114,12 @@ impl Starting { sequencer_channel, shutdown_token, } = self; - let (bundles, executed_blocks, block_commitments, optimistic_blocks) = tokio::try_join!( - open_bundle_stream(rollup_channel.clone()), - open_execute_optimistic_block_stream(rollup_channel.clone()), - open_block_commitment_stream(sequencer_channel.clone()), - open_optimistic_block_stream(sequencer_channel.clone(), rollup_id), - )?; Ok(Running { auctions, - block_commitments, - bundles, - executed_blocks, - optimistic_blocks, + block_commitments: sequencer_channel.open_get_block_commitment_stream(), + bundles: rollup_channel.open_bundle_stream(), + executed_blocks: rollup_channel.open_execute_optimistic_block_stream(), + optimistic_blocks: sequencer_channel.open_get_optimistic_block_stream(rollup_id), rollup_id, shutdown_token, pending_nonce, @@ -141,38 +127,3 @@ impl Starting { .into()) } } - -async fn open_optimistic_block_stream( - chan: SequencerChannel, - rollup_id: RollupId, -) -> eyre::Result { - chan.open_get_optimistic_block_stream(rollup_id) - .await - .wrap_err_with(|| { - format!( - "failed to open optimistic block stream to Sequencer node for rollup ID \ - `{rollup_id}`" - ) - }) -} - -async fn open_block_commitment_stream( - chan: SequencerChannel, -) -> eyre::Result { - chan.open_get_block_commitment_stream() - .await - .wrap_err("failed to open block commitment stream to sequencer node") -} - -async fn open_bundle_stream(chan: RollupChannel) -> eyre::Result { - chan.open_bundle_stream() - .await - .wrap_err("failed to open `bundle stream` to rollup node") -} -async fn open_execute_optimistic_block_stream( - chan: RollupChannel, -) -> eyre::Result { - chan.open_execute_optimistic_block_stream() - .await - .wrap_err("failed to open `execute optimistic block stream` to rollup node") -} diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index b57a161f52..21b4f706a2 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -10,6 +10,7 @@ pub(crate) mod metrics; mod rollup_channel; mod sequencer_channel; mod sequencer_key; +mod streaming_utils; use astria_eyre::{ eyre, diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 1988014b06..54ea01846f 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -1,4 +1,12 @@ -use std::time::Duration; +use std::{ + pin::Pin, + task::{ + ready, + Context, + Poll, + }, + time::Duration, +}; use astria_core::generated::bundle::v1alpha1::{ bundle_service_client::BundleServiceClient, @@ -12,15 +20,19 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::{ + stream::BoxStream, Stream, StreamExt, }; use prost::Name as _; -use tokio::sync::mpsc; -use tokio_stream::wrappers::ReceiverStream; +use tokio::sync::broadcast; +use tokio_stream::wrappers::BroadcastStream; use tonic::transport::Channel; -use crate::bundle::Bundle; +use crate::{ + bundle::Bundle, + streaming_utils::restarting_stream, +}; pub(crate) fn open(endpoint: &str) -> eyre::Result { RollupChannel::create(&endpoint) @@ -45,58 +57,98 @@ impl RollupChannel { }) } - pub(crate) async fn open_bundle_stream(&self) -> eyre::Result { + pub(crate) fn open_bundle_stream(&self) -> BundleStream { use astria_core::generated::bundle::v1alpha1::GetBundleStreamRequest; - let inner = BundleServiceClient::new(self.inner.clone()) - .get_bundle_stream(GetBundleStreamRequest {}) - .await - .wrap_err("failed to open get bundle stream")? - .into_inner(); - Ok(BundleStream { - inner, + let chan = self.inner.clone(); + let inner = restarting_stream(move || { + let chan = chan.clone(); + async move { + let inner = BundleServiceClient::new(chan) + .get_bundle_stream(GetBundleStreamRequest {}) + .await + .map(|rsp| rsp.into_inner()) + // TODO: Don't quietly swallow this error. Provide some form of + // logging. + .ok()?; + Some(InnerBundleStream { + inner, + }) + } }) + .boxed(); + BundleStream { + inner, + } } - pub(crate) async fn open_execute_optimistic_block_stream( - &self, - ) -> eyre::Result { + pub(crate) fn open_execute_optimistic_block_stream(&self) -> ExecuteOptimisticBlockStream { use astria_core::generated::bundle::v1alpha1::{ optimistic_execution_service_client::OptimisticExecutionServiceClient, ExecuteOptimisticBlockStreamRequest, }; - let (to_server_tx, to_server_rx) = mpsc::channel(16); - let out_stream = ReceiverStream::new(to_server_rx).map(|base_block| { - ExecuteOptimisticBlockStreamRequest { - base_block: Some(base_block), + // NOTE: this implementation uses a broadcast channel instead of an mpsc because + // one can get new readers by using Sender::subscribe. This is important for the + // restart mechanism. The mpsc channel (or rather the tokio stream ReceiverStream wrapper) + // would need something ugly like a Arc>, but where + // we'd need to also implement Stream for some wrapper around it.... It's a mess. + let (to_server, _) = broadcast::channel(16); + let chan = self.inner.clone(); + let to_server_2 = to_server.clone(); + let incoming = restarting_stream(move || { + let chan = chan.clone(); + let out_stream = BroadcastStream::new(to_server_2.subscribe()) + // TODO: emit some kind of event when the stream actually starts + // lagging behind instead of quietly discarding the issue. + .filter_map(|maybe_lagged| std::future::ready(maybe_lagged.ok())) + .map(|base_block| ExecuteOptimisticBlockStreamRequest { + base_block: Some(base_block), + }); + + async move { + let inner = OptimisticExecutionServiceClient::new(chan) + .execute_optimistic_block_stream(out_stream) + .await + .map(|rsp| rsp.into_inner()) + // TODO: Don't quietly swallow this error. Provide some form of + // logging. + .ok()?; + Some(InnerExecuteOptimisticBlockStream { + inner, + }) } - }); - let from_server = OptimisticExecutionServiceClient::new(self.inner.clone()) - .execute_optimistic_block_stream(out_stream) - .await - .wrap_err("failed to open execute optimistic block stream")? - .into_inner(); - - Ok(ExecuteOptimisticBlockStream { - incoming: from_server, - outgoing: to_server_tx, }) + .boxed(); + + ExecuteOptimisticBlockStream { + incoming, + outgoing: to_server, + } } } pub(crate) struct BundleStream { - inner: tonic::Streaming, + inner: BoxStream<'static, eyre::Result>, } impl Stream for BundleStream { type Item = eyre::Result; - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_next_unpin(cx) + } +} + +struct InnerBundleStream { + inner: tonic::Streaming, +} + +impl Stream for InnerBundleStream { + type Item = eyre::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let Some(res) = ready!(self.inner.poll_next_unpin(cx)) else { + return Poll::Ready(None); }; let raw = res @@ -116,37 +168,46 @@ impl Stream for BundleStream { ) })?; - std::task::Poll::Ready(Some(Ok(bundle))) + Poll::Ready(Some(Ok(bundle))) } } pub(crate) struct ExecuteOptimisticBlockStream { - incoming: tonic::Streaming, - outgoing: mpsc::Sender, + incoming: BoxStream<'static, eyre::Result>, + outgoing: broadcast::Sender, } impl ExecuteOptimisticBlockStream { /// Immediately sends `base_block` to the connected server. Fails if /// the channel is full. - // NOTE: just leak the tokio mpsc error for now. It's crate private anyway + // NOTE: just leak the tokio error for now. It's crate private anyway // and we'd just end up wrapping the same variants. pub(crate) fn try_send( &mut self, base_block: BaseBlock, - ) -> Result<(), tokio::sync::mpsc::error::TrySendError> { - self.outgoing.try_send(base_block) + ) -> Result<(), broadcast::error::SendError> { + self.outgoing.send(base_block).map(|_| ()) } } impl Stream for ExecuteOptimisticBlockStream { type Item = eyre::Result; - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let Some(message) = std::task::ready!(self.incoming.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.incoming.poll_next_unpin(cx) + } +} + +pub(crate) struct InnerExecuteOptimisticBlockStream { + inner: tonic::Streaming, +} + +impl Stream for InnerExecuteOptimisticBlockStream { + type Item = eyre::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let Some(message) = ready!(self.inner.poll_next_unpin(cx)) else { + return Poll::Ready(None); }; let message = message.wrap_err("failed receiving message over stream")?; @@ -156,6 +217,6 @@ impl Stream for ExecuteOptimisticBlockStream { astria_core::generated::bundle::v1alpha1::ExecuteOptimisticBlockStreamResponse::full_name(), ) })?; - std::task::Poll::Ready(Some(Ok(executed_block))) + Poll::Ready(Some(Ok(executed_block))) } } diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index f759bf0c18..4e543239db 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -1,5 +1,10 @@ use std::{ pin::Pin, + task::{ + ready, + Context, + Poll, + }, time::Duration, }; @@ -23,6 +28,7 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::{ + stream::BoxStream, Future, Stream, StreamExt as _, @@ -30,7 +36,10 @@ use futures::{ use prost::Name; use tonic::transport::Channel; -use crate::block::Commitment; +use crate::{ + block::Commitment, + streaming_utils::restarting_stream, +}; pub(crate) fn open(endpoint: &str) -> eyre::Result { SequencerChannel::create(endpoint) @@ -78,62 +87,89 @@ impl SequencerChannel { } } - pub(crate) async fn open_get_block_commitment_stream( - &self, - ) -> eyre::Result { + pub(crate) fn open_get_block_commitment_stream(&self) -> BlockCommitmentStream { use astria_core::generated::sequencerblock::optimisticblock::v1alpha1:: optimistic_block_service_client::OptimisticBlockServiceClient; - let mut client = OptimisticBlockServiceClient::new(self.inner.clone()); - let stream = client - .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) - .await - .wrap_err("failed to open block commitment stream")? - .into_inner(); - Ok(BlockCommitmentStream::new(stream)) + let chan = self.inner.clone(); + let inner = restarting_stream(move || { + let chan = chan.clone(); + async move { + let inner = OptimisticBlockServiceClient::new(chan) + .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) + .await + // TODO: Don't quietly swallow this error. Provide some form of + // logging. + .ok()? + .into_inner(); + Some(InnerBlockCommitmentStream { + inner, + }) + } + }) + .boxed(); + BlockCommitmentStream { + inner, + } } - pub(crate) async fn open_get_optimistic_block_stream( + pub(crate) fn open_get_optimistic_block_stream( &self, rollup_id: RollupId, - ) -> eyre::Result { + ) -> OptimisticBlockStream { use astria_core::generated::sequencerblock::optimisticblock::v1alpha1::{ optimistic_block_service_client::OptimisticBlockServiceClient, GetOptimisticBlockStreamRequest, }; - let mut client = OptimisticBlockServiceClient::new(self.inner.clone()); - let stream = client - .get_optimistic_block_stream(GetOptimisticBlockStreamRequest { - rollup_id: Some(rollup_id.into_raw()), - }) - .await - .wrap_err("failed to open optimistic block stream")? - .into_inner(); - Ok(OptimisticBlockStream::new(stream)) + + let chan = self.inner.clone(); + let inner = restarting_stream(move || { + let chan = chan.clone(); + async move { + let mut client = OptimisticBlockServiceClient::new(chan); + let inner = client + .get_optimistic_block_stream(GetOptimisticBlockStreamRequest { + rollup_id: Some(rollup_id.into_raw()), + }) + .await + // TODO: Don't quietly swallow this error. Provide some form of + // logging. + .ok()? + .into_inner(); + Some(InnerOptimisticBlockStream { + inner, + }) + } + }) + .boxed(); + OptimisticBlockStream { + inner, + } } } /// A stream for receiving committed blocks from the sequencer. pub(crate) struct BlockCommitmentStream { - inner: tonic::Streaming, + inner: BoxStream<'static, eyre::Result>, } -impl BlockCommitmentStream { - fn new(inner: tonic::Streaming) -> Self { - Self { - inner, - } +impl Stream for BlockCommitmentStream { + type Item = eyre::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_next_unpin(cx) } } -impl Stream for BlockCommitmentStream { +struct InnerBlockCommitmentStream { + inner: tonic::Streaming, +} + +impl Stream for InnerBlockCommitmentStream { type Item = eyre::Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); + return Poll::Ready(None); }; let raw = res @@ -154,34 +190,37 @@ impl Stream for BlockCommitmentStream { ) })?; - std::task::Poll::Ready(Some(Ok(commitment))) + Poll::Ready(Some(Ok(commitment))) } } pub(crate) struct OptimisticBlockStream { - inner: tonic::Streaming, + inner: BoxStream<'static, eyre::Result>, } -impl OptimisticBlockStream { - fn new(inner: tonic::Streaming) -> Self { - Self { - inner, - } +impl Stream for OptimisticBlockStream { + type Item = eyre::Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_next_unpin(cx) } } -impl Stream for OptimisticBlockStream { +struct InnerOptimisticBlockStream { + inner: tonic::Streaming, +} + +impl Stream for InnerOptimisticBlockStream { type Item = eyre::Result; fn poll_next( mut self: Pin<&mut Self>, - cx: &mut std::task::Context, + cx: &mut Context, ) -> std::task::Poll> { - let Some(res) = futures::ready!(self.inner.poll_next_unpin(cx)) else { - return std::task::Poll::Ready(None); + let Some(item) = ready!(self.inner.poll_next_unpin(cx)) else { + return Poll::Ready(None); }; - - let raw = res + let raw = item .wrap_err("failed receiving message over stream")? .block .ok_or_else(|| { diff --git a/crates/astria-auctioneer/src/streaming_utils.rs b/crates/astria-auctioneer/src/streaming_utils.rs new file mode 100644 index 0000000000..b0ffb784d3 --- /dev/null +++ b/crates/astria-auctioneer/src/streaming_utils.rs @@ -0,0 +1,98 @@ +use std::{ + pin::Pin, + task::{ + ready, + Poll, + }, +}; + +use futures::{ + Future, + FutureExt as _, + Stream, + StreamExt as _, +}; +use pin_project_lite::pin_project; + +pub(crate) fn restarting_stream(f: F) -> RestartingStream +where + F: Fn() -> Fut, + Fut: Future>, + S: Stream, +{ + let opening_stream = Some(f()); + RestartingStream { + f, + opening_stream, + running_stream: None, + } +} + +// TODO: Adds logs. +// +// Specifically explain why Fut returns Option, and how to return +// an error to the user (tracing). +pin_project! { + pub(crate) struct RestartingStream { + f: F, + #[pin] + opening_stream: Option, + #[pin] + running_stream: Option, + } +} + +impl Stream for RestartingStream +where + F: Fn() -> Fut, + Fut: Future>, + S: Stream, +{ + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { + let mut this = self.project(); + + if this.opening_stream.is_some() { + debug_assert!(this.running_stream.is_none()); + + let open_output = ready!( + this.opening_stream + .as_mut() + .as_pin_mut() + .expect("inside a branch that checks for opening_stream == Some") + .poll_unpin(cx) + ); + + // The future has completed, unset it so it will not be polled again. + Pin::set(&mut this.opening_stream, None); + match open_output { + Some(stream) => { + Pin::set(&mut this.running_stream, Some(stream)); + } + None => return Poll::Ready(None), + } + } + + if this.running_stream.is_some() { + debug_assert!(this.opening_stream.is_none()); + + match ready!( + this.running_stream + .as_mut() + .as_pin_mut() + .expect("inside a branch that checks running_stream == Some") + .poll_next_unpin(cx) + ) { + Some(item) => return Poll::Ready(Some(item)), + None => { + Pin::set(&mut this.running_stream, None); + Pin::set(&mut this.opening_stream, Some((*this.f)())); + return Poll::Pending; + } + }; + }; + + Poll::Ready(None) + } +} From d27c9f1abf9a439efc0465707f975b9af7306289 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 01:00:22 +0100 Subject: [PATCH 39/73] remove auctioneer runstates stream initialization now happens as part of their impl. separating the auctioneer state into a starting and running phase is no longer necessary. --- .../auctioneer/{inner/running.rs => inner.rs} | 249 +++++++++++------- .../src/auctioneer/inner/mod.rs | 78 ------ .../src/auctioneer/inner/starting.rs | 129 --------- .../astria-auctioneer/src/auctioneer/mod.rs | 2 +- 4 files changed, 156 insertions(+), 302 deletions(-) rename crates/astria-auctioneer/src/auctioneer/{inner/running.rs => inner.rs} (73%) delete mode 100644 crates/astria-auctioneer/src/auctioneer/inner/mod.rs delete mode 100644 crates/astria-auctioneer/src/auctioneer/inner/starting.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner/running.rs b/crates/astria-auctioneer/src/auctioneer/inner.rs similarity index 73% rename from crates/astria-auctioneer/src/auctioneer/inner/running.rs rename to crates/astria-auctioneer/src/auctioneer/inner.rs index 0db5980780..fa985fcd31 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner.rs @@ -1,7 +1,4 @@ -use std::{ - self, - time::Duration, -}; +use std::time::Duration; use astria_core::{ primitive::v1::{ @@ -33,7 +30,6 @@ use tracing::{ warn, }; -use super::RunState; use crate::{ rollup_channel::{ BundleStream, @@ -44,100 +40,86 @@ use crate::{ OptimisticBlockStream, SequencerChannel, }, + sequencer_key::SequencerKey, + Config, + Metrics, }; -#[derive(Clone, Debug)] -pub(crate) struct PendingNonceSubscriber { - inner: tokio::sync::watch::Receiver, -} - -impl PendingNonceSubscriber { - pub(crate) fn get(&self) -> u32 { - *self.inner.borrow() - } -} - -/// Fetches the latest pending nonce for a given address every 500ms. -// TODO: should this provide some kind of feedback mechanism from the -// auction submission? Automatic incrementing for example? Notificatoin -// that the nonce was actually bad? -pub(crate) struct PendingNoncePublisher { - sender: tokio::sync::watch::Sender, - task: JoinHandle<()>, +/// The implementation of the auctioneer business logic. +pub(super) struct Inner { + auctions: crate::auction::Manager, + block_commitments: BlockCommitmentStream, + bundles: BundleStream, + executed_blocks: ExecuteOptimisticBlockStream, + optimistic_blocks: OptimisticBlockStream, + pending_nonce: PendingNoncePublisher, + rollup_id: RollupId, + shutdown_token: CancellationToken, } -impl PendingNoncePublisher { - pub(crate) fn subscribe(&self) -> PendingNonceSubscriber { - PendingNonceSubscriber { - inner: self.sender.subscribe(), +impl Inner { + /// Creates an [`Auctioneer`] service from a [`Config`] and [`Metrics`]. + pub(super) fn new( + config: Config, + metrics: &'static Metrics, + shutdown_token: CancellationToken, + ) -> eyre::Result { + let Config { + sequencer_grpc_endpoint, + sequencer_abci_endpoint, + latency_margin_ms, + rollup_grpc_endpoint, + rollup_id, + sequencer_chain_id, + sequencer_private_key_path, + sequencer_address_prefix, + fee_asset_denomination, + .. + } = config; + + let rollup_id = RollupId::from_unhashed_bytes(rollup_id); + let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; + let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; + + let sequencer_key = SequencerKey::builder() + .path(sequencer_private_key_path) + .prefix(sequencer_address_prefix) + .try_build() + .wrap_err("failed to load sequencer private key")?; + info!(address = %sequencer_key.address(), "loaded sequencer signer"); + + let pending_nonce = + PendingNoncePublisher::new(sequencer_channel.clone(), *sequencer_key.address()); + + // TODO: Rearchitect this thing + let auctions = crate::auction::manager::Builder { + metrics, + sequencer_abci_endpoint, + latency_margin: Duration::from_millis(latency_margin_ms), + sequencer_key: sequencer_key.clone(), + fee_asset_denomination, + sequencer_chain_id, + rollup_id, + pending_nonce: pending_nonce.subscribe(), } + .build() + .wrap_err("failed to initialize auction manager")?; + + Ok(Self { + auctions, + block_commitments: sequencer_channel.open_get_block_commitment_stream(), + bundles: rollup_channel.open_bundle_stream(), + executed_blocks: rollup_channel.open_execute_optimistic_block_stream(), + optimistic_blocks: sequencer_channel.open_get_optimistic_block_stream(rollup_id), + rollup_id, + shutdown_token, + pending_nonce, + }) } - pub(crate) fn new(channel: SequencerChannel, address: Address) -> Self { - use tokio::time::{ - timeout_at, - MissedTickBehavior, - }; - // TODO: make this configurable. Right now they assume a Sequencer block time of 2s, - // so this is fetching nonce up to 4 times a block. - const FETCH_INTERVAL: Duration = Duration::from_millis(500); - const FETCH_TIMEOUT: Duration = FETCH_INTERVAL.saturating_mul(2); - let (tx, _) = tokio::sync::watch::channel(0); - Self { - sender: tx.clone(), - task: tokio::task::spawn(async move { - let mut interval = tokio::time::interval(FETCH_INTERVAL); - interval.set_missed_tick_behavior(MissedTickBehavior::Delay); - let mut fetch = None; - loop { - select!( - instant = interval.tick(), if fetch.is_none() => { - fetch = Some(Box::pin( - timeout_at(instant + FETCH_TIMEOUT, channel.get_pending_nonce(address)))); - } - res = async { fetch.as_mut().unwrap().await }, if fetch.is_some() => { - fetch.take(); - let span = info_span!("fetch pending nonce"); - match res.map_err(eyre::Report::new) { - Ok(Ok(nonce)) => { - info!(nonce, %address, "received new pending from sequencer"); - tx.send_replace(nonce); - } - Ok(Err(error)) | Err(error) => span.in_scope(|| warn!(%error, "failed fetching pending nonce")), - } - } - ) - } - }), - } - } -} - -impl Future for PendingNoncePublisher { - type Output = Result<(), tokio::task::JoinError>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - use futures::FutureExt as _; - self.task.poll_unpin(cx) - } -} - -pub(super) struct Running { - pub(super) auctions: crate::auction::Manager, - pub(super) block_commitments: BlockCommitmentStream, - pub(super) bundles: BundleStream, - pub(super) executed_blocks: ExecuteOptimisticBlockStream, - pub(super) optimistic_blocks: OptimisticBlockStream, - pub(super) pending_nonce: PendingNoncePublisher, - pub(super) rollup_id: RollupId, - pub(super) shutdown_token: CancellationToken, -} - -impl Running { - pub(super) async fn run(mut self) -> eyre::Result { + /// Runs the [`Auctioneer`] service until it received an exit signal, or one of the constituent + /// tasks either ends unexpectedly or returns an error. + pub(super) async fn run(mut self) -> eyre::Result<()> { let reason: eyre::Result<&str> = { // This is a long running loop. Errors are emitted inside the handlers. loop { @@ -262,7 +244,7 @@ impl Running { } #[instrument(skip_all)] - async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result { + async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result<()> { const WAIT_BEFORE_ABORT: Duration = Duration::from_secs(25); // Necessary if we got here because of another reason than receiving an external @@ -278,6 +260,85 @@ impl Running { Err(reason) => error!(%reason, message), }; self.auctions.abort(); - reason.map(|_| RunState::Cancelled) + reason.map(|_| ()) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct PendingNonceSubscriber { + inner: tokio::sync::watch::Receiver, +} + +impl PendingNonceSubscriber { + pub(crate) fn get(&self) -> u32 { + *self.inner.borrow() + } +} + +/// Fetches the latest pending nonce for a given address every 500ms. +// TODO: should this provide some kind of feedback mechanism from the +// auction submission? Automatic incrementing for example? Notificatoin +// that the nonce was actually bad? +struct PendingNoncePublisher { + sender: tokio::sync::watch::Sender, + task: JoinHandle<()>, +} + +impl PendingNoncePublisher { + fn subscribe(&self) -> PendingNonceSubscriber { + PendingNonceSubscriber { + inner: self.sender.subscribe(), + } + } + + pub(crate) fn new(channel: SequencerChannel, address: Address) -> Self { + use tokio::time::{ + timeout_at, + MissedTickBehavior, + }; + // TODO: make this configurable. Right now they assume a Sequencer block time of 2s, + // so this is fetching nonce up to 4 times a block. + const FETCH_INTERVAL: Duration = Duration::from_millis(500); + const FETCH_TIMEOUT: Duration = FETCH_INTERVAL.saturating_mul(2); + let (tx, _) = tokio::sync::watch::channel(0); + Self { + sender: tx.clone(), + task: tokio::task::spawn(async move { + let mut interval = tokio::time::interval(FETCH_INTERVAL); + interval.set_missed_tick_behavior(MissedTickBehavior::Delay); + let mut fetch = None; + loop { + select!( + instant = interval.tick(), if fetch.is_none() => { + fetch = Some(Box::pin( + timeout_at(instant + FETCH_TIMEOUT, channel.get_pending_nonce(address)))); + } + res = async { fetch.as_mut().unwrap().await }, if fetch.is_some() => { + fetch.take(); + let span = info_span!("fetch pending nonce"); + match res.map_err(eyre::Report::new) { + Ok(Ok(nonce)) => { + info!(nonce, %address, "received new pending from sequencer"); + tx.send_replace(nonce); + } + Ok(Err(error)) | Err(error) => span.in_scope(|| warn!(%error, "failed fetching pending nonce")), + } + } + ) + } + }), + } + } +} + +impl Future for PendingNoncePublisher { + type Output = Result<(), tokio::task::JoinError>; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + use futures::FutureExt as _; + self.task.poll_unpin(cx) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs deleted file mode 100644 index 29bf7fda70..0000000000 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use tokio_util::sync::CancellationToken; - -use crate::{ - Config, - Metrics, -}; - -mod running; -mod starting; - -// TODO: Don't export this all the way out of this module -pub(crate) use running::PendingNonceSubscriber; -use running::Running; -use starting::Starting; - -/// The implementation of the auctioneer business logic. -pub(super) struct Inner { - run_state: RunState, -} - -impl Inner { - /// Creates an [`Auctioneer`] service from a [`Config`] and [`Metrics`]. - pub(super) fn new( - cfg: Config, - metrics: &'static Metrics, - shutdown_token: CancellationToken, - ) -> eyre::Result { - let run_state = starting::run_state(cfg, shutdown_token, metrics) - .wrap_err("failed initializating in starting state")?; - Ok(Self { - run_state, - }) - } - - /// Runs the [`Auctioneer`] service until it received an exit signal, or one of the constituent - /// tasks either ends unexpectedly or returns an error. - pub(super) async fn run(self) -> eyre::Result<()> { - let Self { - mut run_state, - } = self; - - loop { - match run_state { - RunState::Cancelled => break Ok(()), - RunState::Starting(starting) => match starting.run().await { - Ok(new_state) => run_state = new_state, - Err(err) => break Err(err).wrap_err("failed during startup"), - }, - RunState::Running(running) => match running.run().await { - Ok(new_state) => run_state = new_state, - Err(err) => break Err(err).wrap_err("failed during execution"), - }, - } - } - } -} - -enum RunState { - Cancelled, - Starting(Starting), - Running(Running), -} - -impl From for RunState { - fn from(value: Running) -> Self { - Self::Running(value) - } -} - -impl From for RunState { - fn from(value: Starting) -> Self { - Self::Starting(value) - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs b/crates/astria-auctioneer/src/auctioneer/inner/starting.rs deleted file mode 100644 index 0882070097..0000000000 --- a/crates/astria-auctioneer/src/auctioneer/inner/starting.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::time::Duration; - -use astria_core::primitive::v1::RollupId; -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use tokio::select; -use tokio_util::sync::CancellationToken; -use tracing::info; - -use super::{ - RunState, - Running, -}; -use crate::{ - auctioneer::inner::running::PendingNoncePublisher, - rollup_channel::RollupChannel, - sequencer_channel::SequencerChannel, - sequencer_key::SequencerKey, - Config, - Metrics, -}; - -pub(super) fn run_state( - config: Config, - shutdown_token: CancellationToken, - metrics: &'static Metrics, -) -> eyre::Result { - Starting::new(config, shutdown_token, metrics).map(Into::into) -} - -pub(super) struct Starting { - auctions: crate::auction::Manager, - pending_nonce: PendingNoncePublisher, - rollup_channel: RollupChannel, - rollup_id: RollupId, - sequencer_channel: SequencerChannel, - shutdown_token: CancellationToken, -} - -impl Starting { - fn new( - config: Config, - shutdown_token: CancellationToken, - metrics: &'static Metrics, - ) -> eyre::Result { - let Config { - sequencer_grpc_endpoint, - sequencer_abci_endpoint, - latency_margin_ms, - rollup_grpc_endpoint, - rollup_id, - sequencer_chain_id, - sequencer_private_key_path, - sequencer_address_prefix, - fee_asset_denomination, - .. - } = config; - - let rollup_id = RollupId::from_unhashed_bytes(rollup_id); - let rollup_channel = crate::rollup_channel::open(&rollup_grpc_endpoint)?; - let sequencer_channel = crate::sequencer_channel::open(&sequencer_grpc_endpoint)?; - - let sequencer_key = SequencerKey::builder() - .path(sequencer_private_key_path) - .prefix(sequencer_address_prefix) - .try_build() - .wrap_err("failed to load sequencer private key")?; - info!(address = %sequencer_key.address(), "loaded sequencer signer"); - - let pending_nonce = - PendingNoncePublisher::new(sequencer_channel.clone(), *sequencer_key.address()); - - // TODO: Rearchitect this thing - let auctions = crate::auction::manager::Builder { - metrics, - sequencer_abci_endpoint, - latency_margin: Duration::from_millis(latency_margin_ms), - sequencer_key: sequencer_key.clone(), - fee_asset_denomination, - sequencer_chain_id, - rollup_id, - pending_nonce: pending_nonce.subscribe(), - } - .build() - .wrap_err("failed to initialize auction manager")?; - - Ok(Starting { - auctions, - pending_nonce, - rollup_channel, - rollup_id, - sequencer_channel, - shutdown_token, - }) - } - - pub(super) async fn run(self) -> eyre::Result { - select!( - biased; - - () = self.shutdown_token.clone().cancelled_owned() => Ok(RunState::Cancelled), - res = self.start_running() => res, - ) - } - - async fn start_running(self) -> eyre::Result { - let Self { - auctions, - pending_nonce, - rollup_channel, - rollup_id, - sequencer_channel, - shutdown_token, - } = self; - Ok(Running { - auctions, - block_commitments: sequencer_channel.open_get_block_commitment_stream(), - bundles: rollup_channel.open_bundle_stream(), - executed_blocks: rollup_channel.open_execute_optimistic_block_stream(), - optimistic_blocks: sequencer_channel.open_get_optimistic_block_stream(rollup_id), - rollup_id, - shutdown_token, - pending_nonce, - } - .into()) - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index 1170f0fcba..e95d1ca9b0 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -6,7 +6,6 @@ use std::{ use astria_eyre::eyre::{ self, }; -use inner::Inner; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; use tracing::instrument; @@ -18,6 +17,7 @@ use crate::{ }; mod inner; +use inner::Inner; pub(crate) use inner::PendingNonceSubscriber; /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. From aa02a7758c8913ddd054267b4568b036323cd81d Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 10:06:07 +0100 Subject: [PATCH 40/73] move auctions, manager into the auctioneer module specifically, make it part of the inner impl. the auction manager is not a lone-standing task but subfunction of the auctioneer/broker. --- .../inner}/auction/allocation_rule.rs | 4 +- .../{ => auctioneer/inner}/auction/builder.rs | 22 +++---- .../{ => auctioneer/inner}/auction/manager.rs | 62 +++++++++++-------- .../src/{ => auctioneer/inner}/auction/mod.rs | 28 ++++----- .../src/auctioneer/{inner.rs => inner/mod.rs} | 12 ++-- .../astria-auctioneer/src/auctioneer/mod.rs | 4 +- crates/astria-auctioneer/src/lib.rs | 1 - 7 files changed, 69 insertions(+), 64 deletions(-) rename crates/astria-auctioneer/src/{ => auctioneer/inner}/auction/allocation_rule.rs (86%) rename crates/astria-auctioneer/src/{ => auctioneer/inner}/auction/builder.rs (77%) rename crates/astria-auctioneer/src/{ => auctioneer/inner}/auction/manager.rs (83%) rename crates/astria-auctioneer/src/{ => auctioneer/inner}/auction/mod.rs (94%) rename crates/astria-auctioneer/src/auctioneer/{inner.rs => inner/mod.rs} (97%) diff --git a/crates/astria-auctioneer/src/auction/allocation_rule.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs similarity index 86% rename from crates/astria-auctioneer/src/auction/allocation_rule.rs rename to crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs index 4eab5a23d5..8b9e4fb05b 100644 --- a/crates/astria-auctioneer/src/auction/allocation_rule.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs @@ -16,7 +16,7 @@ impl FirstPrice { /// Submit a bundle with a bid. /// /// Returns `true` if the bid is accepted as the highest bid. - pub(crate) fn bid(&mut self, bundle: Bundle) -> bool { + pub(super) fn bid(&mut self, bundle: Bundle) -> bool { if bundle.bid() > self.highest_bid.as_ref().map_or(0, Bundle::bid) { self.highest_bid = Some(bundle); true @@ -26,7 +26,7 @@ impl FirstPrice { } /// Returns the winner of the auction, if one exists. - pub(crate) fn winner(self) -> Option { + pub(super) fn winner(self) -> Option { self.highest_bid } } diff --git a/crates/astria-auctioneer/src/auction/builder.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs similarity index 77% rename from crates/astria-auctioneer/src/auction/builder.rs rename to crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs index abc1e59f44..f4e3c18134 100644 --- a/crates/astria-auctioneer/src/auction/builder.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs @@ -10,30 +10,30 @@ use super::{ Auction, Handle, Id, + PendingNonceSubscriber, SequencerKey, }; -use crate::auctioneer::PendingNonceSubscriber; -pub(crate) struct Builder { +pub(in crate::auctioneer) struct Builder { /// The endpoint for the sequencer ABCI service used to submit transactions - pub(crate) sequencer_abci_client: sequencer_client::HttpClient, + pub(super) sequencer_abci_client: sequencer_client::HttpClient, /// The amount of time to wait after a commit before closing the auction for bids and /// submitting the resulting transaction - pub(crate) latency_margin: Duration, + pub(super) latency_margin: Duration, /// The ID of the auction to be run - pub(crate) auction_id: Id, + pub(super) auction_id: Id, /// The key used to sign sequencer transactions - pub(crate) sequencer_key: SequencerKey, + pub(super) sequencer_key: SequencerKey, /// The denomination of the fee asset used in the sequencer transactions - pub(crate) fee_asset_denomination: asset::Denom, + pub(super) fee_asset_denomination: asset::Denom, /// The chain ID used for sequencer transactions - pub(crate) sequencer_chain_id: String, + pub(super) sequencer_chain_id: String, /// The rollup ID used for `RollupDataSubmission` with the auction result - pub(crate) rollup_id: RollupId, - pub(crate) pending_nonce: PendingNonceSubscriber, + pub(super) rollup_id: RollupId, + pub(super) pending_nonce: PendingNonceSubscriber, } impl Builder { - pub(crate) fn build(self) -> (Handle, Auction) { + pub(super) fn build(self) -> (Handle, Auction) { let Self { sequencer_abci_client, latency_margin, diff --git a/crates/astria-auctioneer/src/auction/manager.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs similarity index 83% rename from crates/astria-auctioneer/src/auction/manager.rs rename to crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs index 4100d3cac1..e2eafdc664 100644 --- a/crates/astria-auctioneer/src/auction/manager.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs @@ -20,34 +20,34 @@ use tracing::{ use super::{ Bundle, + PendingNonceSubscriber, SequencerKey, }; use crate::{ - auctioneer::PendingNonceSubscriber, block::Commitment, flatten_join_result, }; -pub(crate) struct Builder { - pub(crate) metrics: &'static crate::Metrics, +pub(in crate::auctioneer::inner) struct Builder { + pub(in crate::auctioneer::inner) metrics: &'static crate::Metrics, /// The ABCI endpoint for the sequencer service used by auctions. - pub(crate) sequencer_abci_endpoint: String, + pub(in crate::auctioneer::inner) sequencer_abci_endpoint: String, /// The amount of time to run the auction timer for. - pub(crate) latency_margin: std::time::Duration, + pub(in crate::auctioneer::inner) latency_margin: std::time::Duration, /// The private key used to sign sequencer transactions. - pub(crate) sequencer_key: SequencerKey, + pub(in crate::auctioneer::inner) sequencer_key: SequencerKey, /// The denomination of the fee asset used in the sequencer transactions - pub(crate) fee_asset_denomination: asset::Denom, + pub(in crate::auctioneer::inner) fee_asset_denomination: asset::Denom, /// The chain ID for sequencer transactions - pub(crate) sequencer_chain_id: String, + pub(in crate::auctioneer::inner) sequencer_chain_id: String, /// The rollup ID for the `RollupDataSubmission`s with auction results - pub(crate) rollup_id: RollupId, - pub(crate) pending_nonce: PendingNonceSubscriber, + pub(in crate::auctioneer::inner) rollup_id: RollupId, + pub(in crate::auctioneer::inner) pending_nonce: PendingNonceSubscriber, } impl Builder { - pub(crate) fn build(self) -> eyre::Result { + pub(in crate::auctioneer::inner) fn build(self) -> eyre::Result { let Self { metrics, sequencer_abci_endpoint, @@ -78,11 +78,11 @@ impl Builder { } struct RunningAuction { - id: crate::auction::Id, + id: super::Id, height: u64, parent_block_of_executed: Option<[u8; 32]>, // TODO: Rename this to AuctionSender or smth like that - sender: crate::auction::Handle, + sender: super::Handle, task: JoinHandle>, } @@ -92,7 +92,7 @@ impl RunningAuction { } } -pub(crate) struct Manager { +pub(in crate::auctioneer::inner) struct Manager { #[allow(dead_code)] metrics: &'static crate::Metrics, sequencer_abci_client: sequencer_client::HttpClient, @@ -106,11 +106,11 @@ pub(crate) struct Manager { } impl Manager { - // pub(crate) fn new_auction(&mut self, auction_id: Id) { + // pub(in crate::auctioneer::inner) fn new_auction(&mut self, auction_id: Id) { // TODO: Add some better instrumentation. #[instrument(skip(self))] - pub(crate) fn new_auction(&mut self, block: FilteredSequencerBlock) { - let new_auction_id = crate::auction::Id::from_sequencer_block_hash(*block.block_hash()); + pub(in crate::auctioneer::inner) fn new_auction(&mut self, block: FilteredSequencerBlock) { + let new_auction_id = super::Id::from_sequencer_block_hash(*block.block_hash()); let height = block.height().into(); if let Some(running_auction) = self.running_auction.take() { @@ -153,10 +153,14 @@ impl Manager { } #[instrument(skip(self))] - // pub(crate) fn start_timer(&mut self, auction_id: Id) -> eyre::Result<()> { - pub(crate) fn start_timer(&mut self, block_commitment: Commitment) -> eyre::Result<()> { + // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(in crate::auctioneer::inner) fn start_timer( + &mut self, + block_commitment: Commitment, + ) -> eyre::Result<()> { let id_according_to_block = - crate::auction::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); + super::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); if let Some(auction) = &mut self.running_auction { if auction.id == id_according_to_block @@ -190,13 +194,14 @@ impl Manager { } #[instrument(skip(self))] - // pub(crate) fn start_processing_bids(&mut self, auction_id: Id) -> eyre::Result<()> { - pub(crate) fn start_processing_bids( + // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(in crate::auctioneer::inner) fn start_processing_bids( &mut self, block: crate::block::Executed, ) -> eyre::Result<()> { let id_according_to_block = - crate::auction::Id::from_sequencer_block_hash(block.sequencer_block_hash()); + super::Id::from_sequencer_block_hash(block.sequencer_block_hash()); if let Some(auction) = &mut self.running_auction { if auction.id == id_according_to_block { @@ -234,9 +239,12 @@ impl Manager { Ok(()) } - pub(crate) fn forward_bundle_to_auction(&mut self, bundle: Bundle) -> eyre::Result<()> { + pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( + &mut self, + bundle: Bundle, + ) -> eyre::Result<()> { let id_according_to_bundle = - crate::auction::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); + super::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); if let Some(auction) = &mut self.running_auction { // TODO: remember to check the parent rollup block hash, i.e.: // @@ -285,12 +293,12 @@ impl Manager { Ok(()) } - pub(crate) async fn next_winner(&mut self) -> Option> { + pub(in crate::auctioneer::inner) async fn next_winner(&mut self) -> Option> { let auction = self.running_auction.as_mut()?; Some(flatten_join_result((&mut auction.task).await)) } - pub(crate) fn abort(&mut self) { + pub(in crate::auctioneer::inner) fn abort(&mut self) { // TODO: Do we need to wait for it to finish? if let Some(auction) = self.running_auction.take() { auction.abort() diff --git a/crates/astria-auctioneer/src/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs similarity index 94% rename from crates/astria-auctioneer/src/auction/mod.rs rename to crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 0992ad1fbf..e46b42622f 100644 --- a/crates/astria-auctioneer/src/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -51,7 +51,7 @@ use astria_eyre::eyre::{ Context, OptionExt as _, }; -pub(crate) use builder::Builder; +pub(super) use builder::Builder; use sequencer_client::SequencerClientExt; use tokio::{ select, @@ -64,19 +64,21 @@ use tracing::{ instrument, }; +use super::PendingNonceSubscriber; use crate::{ - auctioneer::PendingNonceSubscriber, bundle::Bundle, sequencer_key::SequencerKey, }; -pub(crate) mod manager; +mod allocation_rule; +pub(super) mod manager; +pub(super) use manager::Manager; #[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] -pub(crate) struct Id([u8; 32]); +struct Id([u8; 32]); impl Id { - pub(crate) fn from_sequencer_block_hash(block_hash: [u8; 32]) -> Self { + pub(super) fn from_sequencer_block_hash(block_hash: [u8; 32]) -> Self { Self(block_hash) } } @@ -91,29 +93,25 @@ impl std::fmt::Display for Id { } } -pub(crate) use manager::Manager; - -mod allocation_rule; - enum Command { StartProcessingBids, StartTimer, } -pub(crate) struct Handle { +pub(super) struct Handle { commands_tx: mpsc::Sender, new_bundles_tx: mpsc::Sender, } impl Handle { - pub(crate) fn start_processing_bids(&mut self) -> eyre::Result<()> { + pub(super) fn start_processing_bids(&mut self) -> eyre::Result<()> { self.commands_tx .try_send(Command::StartProcessingBids) .wrap_err("unable to send command to start processing bids to auction")?; Ok(()) } - pub(crate) fn start_timer(&mut self) -> eyre::Result<()> { + pub(super) fn start_timer(&mut self) -> eyre::Result<()> { self.commands_tx .try_send(Command::StartTimer) .wrap_err("unable to send command to start time to auction")?; @@ -121,7 +119,7 @@ impl Handle { Ok(()) } - pub(crate) fn try_send_bundle(&mut self, bundle: Bundle) -> eyre::Result<()> { + pub(super) fn try_send_bundle(&mut self, bundle: Bundle) -> eyre::Result<()> { self.new_bundles_tx .try_send(bundle) .wrap_err("bid channel full")?; @@ -130,7 +128,7 @@ impl Handle { } } -pub(crate) struct Auction { +struct Auction { /// The sequencer's ABCI client, used for submitting transactions sequencer_abci_client: sequencer_client::HttpClient, /// Channel for receiving commands sent via the handle @@ -154,7 +152,7 @@ pub(crate) struct Auction { impl Auction { #[instrument(skip_all, fields(id = %self.id))] - pub(crate) async fn run(mut self) -> eyre::Result<()> { + pub(super) async fn run(mut self) -> eyre::Result<()> { let mut latency_margin_timer = None; // TODO: do we want to make this configurable to allow for more complex allocation rules? let mut allocation_rule = FirstPrice::new(); diff --git a/crates/astria-auctioneer/src/auctioneer/inner.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs similarity index 97% rename from crates/astria-auctioneer/src/auctioneer/inner.rs rename to crates/astria-auctioneer/src/auctioneer/inner/mod.rs index fa985fcd31..ffd4893145 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -45,9 +45,11 @@ use crate::{ Metrics, }; +mod auction; + /// The implementation of the auctioneer business logic. pub(super) struct Inner { - auctions: crate::auction::Manager, + auctions: auction::Manager, block_commitments: BlockCommitmentStream, bundles: BundleStream, executed_blocks: ExecuteOptimisticBlockStream, @@ -92,7 +94,7 @@ impl Inner { PendingNoncePublisher::new(sequencer_channel.clone(), *sequencer_key.address()); // TODO: Rearchitect this thing - let auctions = crate::auction::manager::Builder { + let auctions = auction::manager::Builder { metrics, sequencer_abci_endpoint, latency_margin: Duration::from_millis(latency_margin_ms), @@ -265,12 +267,12 @@ impl Inner { } #[derive(Clone, Debug)] -pub(crate) struct PendingNonceSubscriber { +struct PendingNonceSubscriber { inner: tokio::sync::watch::Receiver, } impl PendingNonceSubscriber { - pub(crate) fn get(&self) -> u32 { + fn get(&self) -> u32 { *self.inner.borrow() } } @@ -291,7 +293,7 @@ impl PendingNoncePublisher { } } - pub(crate) fn new(channel: SequencerChannel, address: Address) -> Self { + fn new(channel: SequencerChannel, address: Address) -> Self { use tokio::time::{ timeout_at, MissedTickBehavior, diff --git a/crates/astria-auctioneer/src/auctioneer/mod.rs b/crates/astria-auctioneer/src/auctioneer/mod.rs index e95d1ca9b0..9e443d4d6b 100644 --- a/crates/astria-auctioneer/src/auctioneer/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/mod.rs @@ -17,8 +17,6 @@ use crate::{ }; mod inner; -use inner::Inner; -pub(crate) use inner::PendingNonceSubscriber; /// The [`Auctioneer`] service returned by [`Auctioneer::spawn`]. pub struct Auctioneer { @@ -33,7 +31,7 @@ impl Auctioneer { /// Returns an error if the Auctioneer cannot be initialized. pub fn spawn(cfg: Config, metrics: &'static Metrics) -> eyre::Result { let shutdown_token = CancellationToken::new(); - let inner = Inner::new(cfg, metrics, shutdown_token.child_token())?; + let inner = inner::Inner::new(cfg, metrics, shutdown_token.child_token())?; let task = tokio::spawn(inner.run()); Ok(Self { diff --git a/crates/astria-auctioneer/src/lib.rs b/crates/astria-auctioneer/src/lib.rs index 21b4f706a2..064b50ff7d 100644 --- a/crates/astria-auctioneer/src/lib.rs +++ b/crates/astria-auctioneer/src/lib.rs @@ -1,6 +1,5 @@ //! TODO: Add a description -mod auction; mod auctioneer; mod block; mod build_info; From 19dbdae0220b142de6cf9a25bd96728ff439adb7 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 12:03:10 +0100 Subject: [PATCH 41/73] track the running auction inside auctioneer tracking and delegating to directly to the live auction inside auctioneer simplifies the auction manger significantly, turning it into just an auction factory. --- .../src/auctioneer/inner/auction/factory.rs | 64 ++++ .../src/auctioneer/inner/auction/manager.rs | 307 ------------------ .../src/auctioneer/inner/auction/mod.rs | 8 +- .../src/auctioneer/inner/auction/running.rs | 143 ++++++++ .../src/auctioneer/inner/mod.rs | 72 ++-- 5 files changed, 265 insertions(+), 329 deletions(-) create mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs delete mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs create mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs new file mode 100644 index 0000000000..acef8ad22e --- /dev/null +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -0,0 +1,64 @@ +/// The auction Manager is responsible for managing running auction futures and their +/// associated handles. +use astria_core::{ + primitive::v1::{ + asset, + RollupId, + }, + sequencerblock::v1::block::FilteredSequencerBlock, +}; +use tracing::{ + instrument, + warn, +}; + +use super::{ + PendingNonceSubscriber, + Running, + SequencerKey, +}; + +pub(in crate::auctioneer::inner) struct Factory { + #[allow(dead_code)] + pub(in crate::auctioneer::inner) metrics: &'static crate::Metrics, + pub(in crate::auctioneer::inner) sequencer_abci_client: sequencer_client::HttpClient, + pub(in crate::auctioneer::inner) latency_margin: std::time::Duration, + pub(in crate::auctioneer::inner) sequencer_key: SequencerKey, + pub(in crate::auctioneer::inner) fee_asset_denomination: asset::Denom, + pub(in crate::auctioneer::inner) sequencer_chain_id: String, + pub(in crate::auctioneer::inner) rollup_id: RollupId, + pub(in crate::auctioneer::inner) pending_nonce: PendingNonceSubscriber, +} + +impl Factory { + // pub(in crate::auctioneer::inner) fn new_auction(&mut self, auction_id: Id) { + // TODO: Add some better instrumentation. + #[instrument(skip(self))] + pub(in crate::auctioneer::inner) fn start_new( + &mut self, + block: FilteredSequencerBlock, + ) -> Running { + let new_auction_id = super::Id::from_sequencer_block_hash(*block.block_hash()); + let height = block.height().into(); + + let (handle, auction) = super::Builder { + sequencer_abci_client: self.sequencer_abci_client.clone(), + latency_margin: self.latency_margin, + auction_id: new_auction_id, + sequencer_key: self.sequencer_key.clone(), + fee_asset_denomination: self.fee_asset_denomination.clone(), + sequencer_chain_id: self.sequencer_chain_id.clone(), + rollup_id: self.rollup_id, + pending_nonce: self.pending_nonce.clone(), + } + .build(); + + Running { + id: new_auction_id, + height, + parent_block_of_executed: None, + sender: handle, + task: tokio::task::spawn(auction.run()), + } + } +} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs deleted file mode 100644 index e2eafdc664..0000000000 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/manager.rs +++ /dev/null @@ -1,307 +0,0 @@ -/// The auction Manager is responsible for managing running auction futures and their -/// associated handles. -use astria_core::{ - primitive::v1::{ - asset, - RollupId, - }, - sequencerblock::v1::block::FilteredSequencerBlock, -}; -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use tokio::task::JoinHandle; -use tracing::{ - info, - instrument, - warn, -}; - -use super::{ - Bundle, - PendingNonceSubscriber, - SequencerKey, -}; -use crate::{ - block::Commitment, - flatten_join_result, -}; - -pub(in crate::auctioneer::inner) struct Builder { - pub(in crate::auctioneer::inner) metrics: &'static crate::Metrics, - - /// The ABCI endpoint for the sequencer service used by auctions. - pub(in crate::auctioneer::inner) sequencer_abci_endpoint: String, - /// The amount of time to run the auction timer for. - pub(in crate::auctioneer::inner) latency_margin: std::time::Duration, - /// The private key used to sign sequencer transactions. - pub(in crate::auctioneer::inner) sequencer_key: SequencerKey, - /// The denomination of the fee asset used in the sequencer transactions - pub(in crate::auctioneer::inner) fee_asset_denomination: asset::Denom, - /// The chain ID for sequencer transactions - pub(in crate::auctioneer::inner) sequencer_chain_id: String, - /// The rollup ID for the `RollupDataSubmission`s with auction results - pub(in crate::auctioneer::inner) rollup_id: RollupId, - pub(in crate::auctioneer::inner) pending_nonce: PendingNonceSubscriber, -} - -impl Builder { - pub(in crate::auctioneer::inner) fn build(self) -> eyre::Result { - let Self { - metrics, - sequencer_abci_endpoint, - latency_margin, - fee_asset_denomination, - rollup_id, - sequencer_key, - sequencer_chain_id, - pending_nonce, - } = self; - - let sequencer_abci_client = - sequencer_client::HttpClient::new(sequencer_abci_endpoint.as_str()) - .wrap_err("failed constructing sequencer abci client")?; - - Ok(Manager { - metrics, - sequencer_abci_client, - latency_margin, - running_auction: None, - sequencer_key, - fee_asset_denomination, - sequencer_chain_id, - rollup_id, - pending_nonce, - }) - } -} - -struct RunningAuction { - id: super::Id, - height: u64, - parent_block_of_executed: Option<[u8; 32]>, - // TODO: Rename this to AuctionSender or smth like that - sender: super::Handle, - task: JoinHandle>, -} - -impl RunningAuction { - fn abort(&self) { - self.task.abort() - } -} - -pub(in crate::auctioneer::inner) struct Manager { - #[allow(dead_code)] - metrics: &'static crate::Metrics, - sequencer_abci_client: sequencer_client::HttpClient, - latency_margin: std::time::Duration, - running_auction: Option, - sequencer_key: SequencerKey, - fee_asset_denomination: asset::Denom, - sequencer_chain_id: String, - rollup_id: RollupId, - pending_nonce: PendingNonceSubscriber, -} - -impl Manager { - // pub(in crate::auctioneer::inner) fn new_auction(&mut self, auction_id: Id) { - // TODO: Add some better instrumentation. - #[instrument(skip(self))] - pub(in crate::auctioneer::inner) fn new_auction(&mut self, block: FilteredSequencerBlock) { - let new_auction_id = super::Id::from_sequencer_block_hash(*block.block_hash()); - let height = block.height().into(); - - if let Some(running_auction) = self.running_auction.take() { - // NOTE: We just throw away the old auction after aborting it. Is there - // value in `.join`ing it after the abort except for ensuring that it - // did indeed abort? What if the running auction is not tracked inside - // the "auction manager", but the auction manager is turned into a simpler - // factory so that the running auction is running inside the auctioneer? - // Then the auctioneer would always have the previous/old auction and could - // decide what to do with it. That might make this a cleaner implementation. - let old_auction_id = running_auction.id; - info!( - %new_auction_id, - %old_auction_id, - "received optimistic block, aborting old auction and starting new auction" - ); - - running_auction.abort(); - } - - let (handle, auction) = super::Builder { - sequencer_abci_client: self.sequencer_abci_client.clone(), - latency_margin: self.latency_margin, - auction_id: new_auction_id, - sequencer_key: self.sequencer_key.clone(), - fee_asset_denomination: self.fee_asset_denomination.clone(), - sequencer_chain_id: self.sequencer_chain_id.clone(), - rollup_id: self.rollup_id, - pending_nonce: self.pending_nonce.clone(), - } - .build(); - - self.running_auction = Some(RunningAuction { - id: new_auction_id, - height, - parent_block_of_executed: None, - sender: handle, - task: tokio::task::spawn(auction.run()), - }); - } - - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> - // eyre::Result<()> { - pub(in crate::auctioneer::inner) fn start_timer( - &mut self, - block_commitment: Commitment, - ) -> eyre::Result<()> { - let id_according_to_block = - super::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); - - if let Some(auction) = &mut self.running_auction { - if auction.id == id_according_to_block - && block_commitment.sequencer_height() == auction.height - { - auction - .sender - .start_timer() - .wrap_err("failed to send command to start timer to auction")?; - } else { - // TODO: provide better information on the blocks/currently running auction. - // warn!( - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // block_commitment.sequencer_block_hash = - // %base64(block_commitment.sequencer_block_hash()), "received - // block commitment for the wrong block" ); - info!( - "not starting the auction timer because sequencer block hash and height of \ - the commitment did not match that of the running auction", - ); - } - } else { - info!( - "cannot start the auction timer with the received executed block because no \ - auction was currently running; dropping the commit block", - ); - } - - Ok(()) - } - - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> - // eyre::Result<()> { - pub(in crate::auctioneer::inner) fn start_processing_bids( - &mut self, - block: crate::block::Executed, - ) -> eyre::Result<()> { - let id_according_to_block = - super::Id::from_sequencer_block_hash(block.sequencer_block_hash()); - - if let Some(auction) = &mut self.running_auction { - if auction.id == id_according_to_block { - // TODO: What if it was already set? Overwrite? Replace? Drop? - let _ = auction - .parent_block_of_executed - .replace(block.parent_rollup_block_hash()); - auction - .sender - .start_processing_bids() - .wrap_err("failed to send command to start processing bids")?; - } else { - // TODO: bring back the fields to track the dropped block and current block - // warn!( - // // TODO: nicer display for the current block - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // executed_block.sequencer_block_hash = - // %base64(executed_block.sequencer_block_hash()), - // executed_block.rollup_block_hash = - // %base64(executed_block.rollup_block_hash()), "received - // optimistic execution result for wrong sequencer block" ); - warn!( - "not starting to process bids in the current auction because we received an \ - executed block from the rollup with a sequencer block hash that does not \ - match that of the currently running auction; dropping the executed block" - ); - } - } else { - info!( - "cannot start processing bids with the received executed block because no auction \ - was currently running; dropping the executed block" - ); - } - Ok(()) - } - - pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( - &mut self, - bundle: Bundle, - ) -> eyre::Result<()> { - let id_according_to_bundle = - super::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); - if let Some(auction) = &mut self.running_auction { - // TODO: remember to check the parent rollup block hash, i.e.: - // - // if let Some(bundle.rollup_parent_block_hash) = - // current_auction/block.parent_rollup_block_hash() { ensure!( - // bundle.parent_rollup_block_hash() == rollup_parent_block_hash, - // "bundle's rollup parent block hash {bundle_hash} does not match current - // rollup \ parent block hash {current_hash}", - // bundle_hash = base64(bundle.parent_rollup_block_hash()), - // current_hash = base64(rollup_parent_block_hash) - // ); - // } - let Some(parent_block_of_executed) = auction.parent_block_of_executed else { - eyre::bail!( - "received a new bundle but the current auction has not yet - received an execute block from the rollup; dropping the bundle" - ); - }; - let ids_match = auction.id == id_according_to_bundle; - let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); - if ids_match && parent_blocks_match { - auction - .sender - .try_send_bundle(bundle) - .wrap_err("failed to add bundle to auction")?; - } else { - warn!( - // TODO: Add these fields back in. Is it even necessary to return the error? - // Can't we just fire the event here? necessary? - // - // curent_block.sequencer_block_hash = %base64(self. - // current_block.sequencer_block_hash()), - // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - // bundle.parent_rollup_block_hash = - // %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - eyre::bail!("auction ID and ID according to bundle don't match"); - } - } else { - info!( - "cannot forward the received bundle to an auction because no auction is currently \ - running; dropping the bundle" - ); - } - Ok(()) - } - - pub(in crate::auctioneer::inner) async fn next_winner(&mut self) -> Option> { - let auction = self.running_auction.as_mut()?; - Some(flatten_join_result((&mut auction.task).await)) - } - - pub(in crate::auctioneer::inner) fn abort(&mut self) { - // TODO: Do we need to wait for it to finish? - if let Some(auction) = self.running_auction.take() { - auction.abort() - } - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index e46b42622f..af15b26889 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -37,7 +37,6 @@ //! as it received the signal to start the timer. This corresponds to the sequencer block being //! committed, thus providing the latest pending nonce. -mod builder; use std::time::Duration; use allocation_rule::FirstPrice; @@ -71,8 +70,11 @@ use crate::{ }; mod allocation_rule; -pub(super) mod manager; -pub(super) use manager::Manager; +mod builder; +pub(super) mod factory; +mod running; +pub(super) use factory::Factory; +pub(super) use running::Running; #[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] struct Id([u8; 32]); diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs new file mode 100644 index 0000000000..f635c6e874 --- /dev/null +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -0,0 +1,143 @@ +use astria_eyre::eyre::{ + self, + WrapErr as _, +}; +use tokio::task::JoinHandle; +use tracing::{ + info, + instrument, + warn, +}; + +use crate::{ + block::Commitment, + bundle::Bundle, + flatten_join_result, +}; + +pub(in crate::auctioneer::inner) struct Running { + pub(super) id: super::Id, + pub(super) height: u64, + pub(super) parent_block_of_executed: Option<[u8; 32]>, + // TODO: Rename this to AuctionSender or smth like that + pub(super) sender: super::Handle, + pub(super) task: JoinHandle>, +} + +impl Running { + pub(in crate::auctioneer::inner) fn abort(&self) { + self.task.abort() + } + + #[instrument(skip(self))] + // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(in crate::auctioneer::inner) fn start_timer( + &mut self, + block_commitment: Commitment, + ) -> eyre::Result<()> { + let id_according_to_block = + super::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); + + if self.id == id_according_to_block && block_commitment.sequencer_height() == self.height { + self.sender + .start_timer() + .wrap_err("failed to send command to start timer to auction")?; + } else { + // TODO: provide better information on the blocks/currently running auction. + // warn!( + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // block_commitment.sequencer_block_hash = + // %base64(block_commitment.sequencer_block_hash()), "received + // block commitment for the wrong block" ); + info!( + "not starting the auction timer because sequencer block hash and height of the \ + commitment did not match that of the running auction", + ); + } + + Ok(()) + } + + #[instrument(skip(self))] + // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(in crate::auctioneer::inner) fn start_processing_bids( + &mut self, + block: crate::block::Executed, + ) -> eyre::Result<()> { + let id_according_to_block = + super::Id::from_sequencer_block_hash(block.sequencer_block_hash()); + + if self.id == id_according_to_block { + // TODO: What if it was already set? Overwrite? Replace? Drop? + let _ = self + .parent_block_of_executed + .replace(block.parent_rollup_block_hash()); + self.sender + .start_processing_bids() + .wrap_err("failed to send command to start processing bids")?; + } else { + // TODO: bring back the fields to track the dropped block and current block + // warn!( + // // TODO: nicer display for the current block + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // executed_block.sequencer_block_hash = + // %base64(executed_block.sequencer_block_hash()), + // executed_block.rollup_block_hash = + // %base64(executed_block.rollup_block_hash()), "received + // optimistic execution result for wrong sequencer block" ); + warn!( + "not starting to process bids in the current auction because we received an \ + executed block from the rollup with a sequencer block hash that does not match \ + that of the currently running auction; dropping the executed block" + ); + } + + Ok(()) + } + + pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( + &mut self, + bundle: Bundle, + ) -> eyre::Result<()> { + let id_according_to_bundle = + super::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); + + // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, + // tacked block hash, provided block hash, etc. + let Some(parent_block_of_executed) = self.parent_block_of_executed else { + eyre::bail!( + "received a new bundle but the current auction has not yet + received an execute block from the rollup; dropping the bundle" + ); + }; + let ids_match = self.id == id_according_to_bundle; + let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); + if ids_match && parent_blocks_match { + self.sender + .try_send_bundle(bundle) + .wrap_err("failed to add bundle to auction")?; + } else { + warn!( + // TODO: Add these fields back in. Is it even necessary to return the error? + // Can't we just fire the event here? necessary? + // + // curent_block.sequencer_block_hash = %base64(self. + // current_block.sequencer_block_hash()), + // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), + // bundle.parent_rollup_block_hash = + // %base64(bundle.parent_rollup_block_hash()), + "incoming bundle does not match current block, ignoring" + ); + eyre::bail!("auction ID and ID according to bundle don't match"); + } + Ok(()) + } + + pub(in crate::auctioneer::inner) async fn next_winner(&mut self) -> eyre::Result<()> { + flatten_join_result((&mut self.task).await) + } +} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index ffd4893145..f36aa73b19 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -49,10 +49,11 @@ mod auction; /// The implementation of the auctioneer business logic. pub(super) struct Inner { - auctions: auction::Manager, + auction_factory: auction::Factory, block_commitments: BlockCommitmentStream, bundles: BundleStream, executed_blocks: ExecuteOptimisticBlockStream, + running_auction: Option, optimistic_blocks: OptimisticBlockStream, pending_nonce: PendingNoncePublisher, rollup_id: RollupId, @@ -93,27 +94,30 @@ impl Inner { let pending_nonce = PendingNoncePublisher::new(sequencer_channel.clone(), *sequencer_key.address()); + let sequencer_abci_client = + sequencer_client::HttpClient::new(sequencer_abci_endpoint.as_str()) + .wrap_err("failed constructing sequencer abci client")?; + // TODO: Rearchitect this thing - let auctions = auction::manager::Builder { + let auction_factory = auction::Factory { metrics, - sequencer_abci_endpoint, + sequencer_abci_client, latency_margin: Duration::from_millis(latency_margin_ms), sequencer_key: sequencer_key.clone(), fee_asset_denomination, sequencer_chain_id, rollup_id, pending_nonce: pending_nonce.subscribe(), - } - .build() - .wrap_err("failed to initialize auction manager")?; + }; Ok(Self { - auctions, + auction_factory, block_commitments: sequencer_channel.open_get_block_commitment_stream(), bundles: rollup_channel.open_bundle_stream(), executed_blocks: rollup_channel.open_execute_optimistic_block_stream(), optimistic_blocks: sequencer_channel.open_get_optimistic_block_stream(rollup_id), rollup_id, + running_auction: None, shutdown_token, pending_nonce, }) @@ -161,7 +165,7 @@ impl Inner { let _ = self.handle_executed_block(res); } - Some(res) = self.auctions.next_winner() => { + res = async { self.running_auction.as_mut().unwrap().next_winner().await }, if self.running_auction.is_some() => { let _ = self.handle_auction_winner(res); } @@ -194,7 +198,23 @@ impl Inner { optimistic_block.wrap_err("encountered problem receiving optimistic block message")?; // FIXME: Don't clone this; find a better way. - self.auctions.new_auction(optimistic_block.clone()); + let new_auction = self.auction_factory.start_new(optimistic_block.clone()); + if let Some(old_auction) = self.running_auction.replace(new_auction) { + // NOTE: We just throw away the old auction after aborting it. Is there + // value in `.join`ing it after the abort except for ensuring that it + // did indeed abort? What if the running auction is not tracked inside + // the "auction manager", but the auction manager is turned into a simpler + // factory so that the running auction is running inside the auctioneer? + // Then the auctioneer would always have the previous/old auction and could + // decide what to do with it. That might make this a cleaner implementation. + // let old_auction_id = running_auction.id; + // info!( + // %new_auction_id, + // %old_auction_id, + // "received optimistic block, aborting old auction and starting new auction" + // ); + old_auction.abort(); + } let base_block = crate::block::Optimistic::new(optimistic_block) .try_into_base_block(self.rollup_id) @@ -215,9 +235,13 @@ impl Inner { ) -> eyre::Result<()> { let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; - self.auctions - .start_timer(block_commitment) - .wrap_err("failed to start timer")?; + if let Some(running_auction) = &mut self.running_auction { + running_auction + .start_timer(block_commitment) + .wrap_err("failed to start timer")?; + } else { + // TODO: emit an event? + } Ok(()) } @@ -229,9 +253,13 @@ impl Inner { executed_block: eyre::Result, ) -> eyre::Result<()> { let executed_block = executed_block.wrap_err("failed to receive executed block")?; - self.auctions - .start_processing_bids(executed_block) - .wrap_err("failed to start processing bids")?; + if let Some(running_auction) = &mut self.running_auction { + running_auction + .start_processing_bids(executed_block) + .wrap_err("failed to start processing bids")?; + } else { + // TODO: emit an event? + } Ok(()) } @@ -239,9 +267,13 @@ impl Inner { // %base64(self.current_block.sequencer_block_hash())))] fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { let bundle = bundle.wrap_err("received problematic bundle")?; - self.auctions - .forward_bundle_to_auction(bundle) - .wrap_err("failed to forward bundle to auction")?; + if let Some(running_auction) = &mut self.running_auction { + running_auction + .forward_bundle_to_auction(bundle) + .wrap_err("failed to forward bundle to auction")?; + } else { + // TODO: emit an event? + } Ok(()) } @@ -261,7 +293,9 @@ impl Inner { Ok(reason) => info!(%reason, message), Err(reason) => error!(%reason, message), }; - self.auctions.abort(); + if let Some(running_auction) = self.running_auction.take() { + running_auction.abort(); + } reason.map(|_| ()) } } From 9a3a1b73f28f48327e1000a2b575f15b4f77d81f Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 13:10:02 +0100 Subject: [PATCH 42/73] turn auction::Running into a future --- .../src/auctioneer/inner/auction/running.rs | 16 ++++++++++++++-- .../src/auctioneer/inner/mod.rs | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs index f635c6e874..d1e7bfe5b0 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -2,6 +2,10 @@ use astria_eyre::eyre::{ self, WrapErr as _, }; +use futures::{ + Future, + FutureExt, +}; use tokio::task::JoinHandle; use tracing::{ info, @@ -136,8 +140,16 @@ impl Running { } Ok(()) } +} + +impl Future for Running { + type Output = eyre::Result<()>; - pub(in crate::auctioneer::inner) async fn next_winner(&mut self) -> eyre::Result<()> { - flatten_join_result((&mut self.task).await) + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let res = std::task::ready!(self.task.poll_unpin(cx)); + std::task::Poll::Ready(flatten_join_result(res)) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index f36aa73b19..efbe96f68d 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -165,8 +165,8 @@ impl Inner { let _ = self.handle_executed_block(res); } - res = async { self.running_auction.as_mut().unwrap().next_winner().await }, if self.running_auction.is_some() => { - let _ = self.handle_auction_winner(res); + res = async { self.running_auction.as_mut().unwrap().await }, if self.running_auction.is_some() => { + let _ = self.handle_auction_result(res); } Some(res) = self.bundles.next() => { @@ -184,7 +184,7 @@ impl Inner { } #[instrument(skip_all, err)] - fn handle_auction_winner(&self, res: eyre::Result<()>) -> eyre::Result<()> { + fn handle_auction_result(&self, res: eyre::Result<()>) -> eyre::Result<()> { res } From 444e65531d76be7e87d58e522095bcd5aff7284c Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 13:32:54 +0100 Subject: [PATCH 43/73] clippy and toml formatting --- crates/astria-auctioneer/Cargo.toml | 3 ++- .../src/auctioneer/inner/auction/mod.rs | 2 +- .../src/auctioneer/inner/auction/running.rs | 2 +- .../src/auctioneer/inner/mod.rs | 2 +- crates/astria-auctioneer/src/bundle/mod.rs | 4 ++-- crates/astria-auctioneer/src/main.rs | 2 +- crates/astria-auctioneer/src/rollup_channel.rs | 6 +++--- .../astria-auctioneer/src/sequencer_channel.rs | 3 ++- crates/astria-auctioneer/src/streaming_utils.rs | 17 ++++++++--------- 9 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 9a64b531c2..4d6998664b 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -18,6 +18,7 @@ telemetry = { package = "astria-telemetry", path = "../astria-telemetry", featur "display", ] } +base64 = { workspace = true } bytes = { workspace = true } futures = { workspace = true } hex = { workspace = true } @@ -39,7 +40,7 @@ tracing = { workspace = true, features = ["attributes"] } tryhard = { workspace = true } tonic = { workspace = true } tokio-stream = { workspace = true, features = ["sync"] } -base64.workspace = true + pin-project-lite = "0.2.15" [dev-dependencies] diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index af15b26889..fee42460d3 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -214,7 +214,7 @@ impl Auction { .into_transaction_body( self.pending_nonce.get(), self.rollup_id, - self.sequencer_key.clone(), + &self.sequencer_key, self.fee_asset_denomination.clone(), self.sequencer_chain_id, ); diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs index d1e7bfe5b0..72f680d334 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -30,7 +30,7 @@ pub(in crate::auctioneer::inner) struct Running { impl Running { pub(in crate::auctioneer::inner) fn abort(&self) { - self.task.abort() + self.task.abort(); } #[instrument(skip(self))] diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index efbe96f68d..28ce8d237a 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -360,7 +360,7 @@ impl PendingNoncePublisher { Ok(Err(error)) | Err(error) => span.in_scope(|| warn!(%error, "failed fetching pending nonce")), } } - ) + ); } }), } diff --git a/crates/astria-auctioneer/src/bundle/mod.rs b/crates/astria-auctioneer/src/bundle/mod.rs index b1b1c4f5bc..748bb55d14 100644 --- a/crates/astria-auctioneer/src/bundle/mod.rs +++ b/crates/astria-auctioneer/src/bundle/mod.rs @@ -74,7 +74,7 @@ impl Bundle { self, nonce: u32, rollup_id: RollupId, - sequencer_key: SequencerKey, + sequencer_key: &SequencerKey, fee_asset: asset::Denom, chain_id: String, ) -> TransactionBody { @@ -117,7 +117,7 @@ pub(crate) struct Allocation { } impl Allocation { - fn new(bundle: Bundle, sequencer_key: SequencerKey) -> Self { + fn new(bundle: Bundle, sequencer_key: &SequencerKey) -> Self { let bundle_data = bundle.clone().into_raw().encode_to_vec(); let signature = sequencer_key.signing_key().sign(&bundle_data); let verification_key = sequencer_key.signing_key().verification_key(); diff --git a/crates/astria-auctioneer/src/main.rs b/crates/astria-auctioneer/src/main.rs index 7032e4477b..326cf7b22f 100644 --- a/crates/astria-auctioneer/src/main.rs +++ b/crates/astria-auctioneer/src/main.rs @@ -97,7 +97,7 @@ async fn shutdown(reason: eyre::Result<&'static str>, mut service: Auctioneer) - Ok(reason) => { info!(reason, message); if let Err(error) = service.shutdown().await { - warn!(%error, "encountered errors during shutdown") + warn!(%error, "encountered errors during shutdown"); }; ExitCode::SUCCESS } diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 54ea01846f..1e00ed8c09 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -35,7 +35,7 @@ use crate::{ }; pub(crate) fn open(endpoint: &str) -> eyre::Result { - RollupChannel::create(&endpoint) + RollupChannel::create(endpoint) .wrap_err_with(|| format!("failed to create a gRPC channel to rollup at `{endpoint}`")) } @@ -66,7 +66,7 @@ impl RollupChannel { let inner = BundleServiceClient::new(chan) .get_bundle_stream(GetBundleStreamRequest {}) .await - .map(|rsp| rsp.into_inner()) + .map(tonic::Response::into_inner) // TODO: Don't quietly swallow this error. Provide some form of // logging. .ok()?; @@ -109,7 +109,7 @@ impl RollupChannel { let inner = OptimisticExecutionServiceClient::new(chan) .execute_optimistic_block_stream(out_stream) .await - .map(|rsp| rsp.into_inner()) + .map(tonic::Response::into_inner) // TODO: Don't quietly swallow this error. Provide some form of // logging. .ok()?; diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 4e543239db..e5e28ece99 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -168,6 +168,8 @@ impl Stream for InnerBlockCommitmentStream { type Item = eyre::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use astria_core::generated::sequencerblock::optimisticblock::v1alpha1 as raw; + let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { return Poll::Ready(None); }; @@ -182,7 +184,6 @@ impl Stream for InnerBlockCommitmentStream { ) })?; - use astria_core::generated::sequencerblock::optimisticblock::v1alpha1 as raw; let commitment = Commitment::try_from_raw(&raw).wrap_err_with(|| { format!( "failed to validate message `{}` received from server", diff --git a/crates/astria-auctioneer/src/streaming_utils.rs b/crates/astria-auctioneer/src/streaming_utils.rs index b0ffb784d3..cad02ca32e 100644 --- a/crates/astria-auctioneer/src/streaming_utils.rs +++ b/crates/astria-auctioneer/src/streaming_utils.rs @@ -77,21 +77,20 @@ where if this.running_stream.is_some() { debug_assert!(this.opening_stream.is_none()); - match ready!( + if let Some(item) = ready!( this.running_stream .as_mut() .as_pin_mut() .expect("inside a branch that checks running_stream == Some") .poll_next_unpin(cx) ) { - Some(item) => return Poll::Ready(Some(item)), - None => { - Pin::set(&mut this.running_stream, None); - Pin::set(&mut this.opening_stream, Some((*this.f)())); - return Poll::Pending; - } - }; - }; + return Poll::Ready(Some(item)); + } + + Pin::set(&mut this.running_stream, None); + Pin::set(&mut this.opening_stream, Some((*this.f)())); + return Poll::Pending; + } Poll::Ready(None) } From 3116bd2db85aeb6ea507fb4e1b3d34215b8dc3d8 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 15:50:34 +0100 Subject: [PATCH 44/73] feat(core)!: provide a newtype for block hashes --- .../src/celestia/block_verifier.rs | 9 ++- crates/astria-conductor/src/celestia/mod.rs | 13 +++- .../src/celestia/reconstruct.rs | 8 +- .../src/celestia/reporting.rs | 3 +- .../astria-conductor/src/celestia/verify.rs | 19 +++-- crates/astria-conductor/src/executor/mod.rs | 13 ++-- .../tests/blackbox/helpers/mod.rs | 4 +- .../src/sequencerblock/v1/block.rs | 77 ++++++++++++++++--- .../src/sequencerblock/v1/celestia.rs | 17 ++-- crates/astria-sequencer/src/grpc/sequencer.rs | 2 +- crates/astria-sequencer/src/grpc/state_ext.rs | 13 ++-- .../src/grpc/storage/values/block_hash.rs | 8 +- 12 files changed, 131 insertions(+), 55 deletions(-) diff --git a/crates/astria-conductor/src/celestia/block_verifier.rs b/crates/astria-conductor/src/celestia/block_verifier.rs index ff72aa8a7c..d24582b424 100644 --- a/crates/astria-conductor/src/celestia/block_verifier.rs +++ b/crates/astria-conductor/src/celestia/block_verifier.rs @@ -224,7 +224,10 @@ mod tests { generated::sequencerblock::v1::SequencerBlockHeader as RawSequencerBlockHeader, primitive::v1::RollupId, sequencerblock::v1::{ - block::SequencerBlockHeader, + block::{ + BlockHash, + SequencerBlockHeader, + }, celestia::UncheckedSubmittedMetadata, }, }; @@ -345,7 +348,7 @@ mod tests { let header = SequencerBlockHeader::try_from_raw(header).unwrap(); let sequencer_blob = UncheckedSubmittedMetadata { - block_hash: [0u8; 32], + block_hash: BlockHash::new([0u8; 32]), header, rollup_ids: vec![], rollup_transactions_proof, @@ -390,7 +393,7 @@ mod tests { let header = SequencerBlockHeader::try_from_raw(header).unwrap(); let sequencer_blob = UncheckedSubmittedMetadata { - block_hash: [0u8; 32], + block_hash: BlockHash::new([0u8; 32]), header, rollup_ids: vec![rollup_id], rollup_transactions_proof, diff --git a/crates/astria-conductor/src/celestia/mod.rs b/crates/astria-conductor/src/celestia/mod.rs index 85146dc645..44c5924208 100644 --- a/crates/astria-conductor/src/celestia/mod.rs +++ b/crates/astria-conductor/src/celestia/mod.rs @@ -6,7 +6,10 @@ use std::{ use astria_core::{ primitive::v1::RollupId, - sequencerblock::v1::block::SequencerBlockHeader, + sequencerblock::v1::block::{ + BlockHash, + SequencerBlockHeader, + }, }; use astria_eyre::eyre::{ self, @@ -103,12 +106,16 @@ use crate::{ #[derive(Clone, Debug)] pub(crate) struct ReconstructedBlock { pub(crate) celestia_height: u64, - pub(crate) block_hash: [u8; 32], + pub(crate) block_hash: BlockHash, pub(crate) header: SequencerBlockHeader, pub(crate) transactions: Vec, } impl ReconstructedBlock { + pub(crate) fn block_hash(&self) -> &BlockHash { + &self.block_hash + } + pub(crate) fn sequencer_height(&self) -> SequencerHeight { self.header.height() } @@ -458,7 +465,7 @@ impl RunningReader { error = %eyre::Report::new(e), source_celestia_height = celestia_height, sequencer_height, - block_hash = %base64(&block_hash), + block_hash = %block_hash, "failed pushing reconstructed block into sequential cache; dropping it", ); } diff --git a/crates/astria-conductor/src/celestia/reconstruct.rs b/crates/astria-conductor/src/celestia/reconstruct.rs index 3ee84f93be..a66dbfc8ef 100644 --- a/crates/astria-conductor/src/celestia/reconstruct.rs +++ b/crates/astria-conductor/src/celestia/reconstruct.rs @@ -3,12 +3,12 @@ use std::collections::HashMap; use astria_core::{ primitive::v1::RollupId, sequencerblock::v1::{ + block::BlockHash, celestia::UncheckedSubmittedMetadata, SubmittedMetadata, SubmittedRollupData, }, }; -use telemetry::display::base64; use tracing::{ info, warn, @@ -72,7 +72,7 @@ pub(super) fn reconstruct_blocks_from_verified_blobs( "no sequencer header blob matching the rollup blob's block hash found" }; info!( - block_hash = %base64(&rollup.sequencer_block_hash()), + block_hash = %rollup.sequencer_block_hash(), reason, "dropping rollup blob", ); @@ -83,7 +83,7 @@ pub(super) fn reconstruct_blocks_from_verified_blobs( for header_blob in header_blobs.into_values() { if header_blob.contains_rollup_id(rollup_id) { warn!( - block_hash = %base64(header_blob.block_hash()), + block_hash = %header_blob.block_hash(), "sequencer header blob contains the target rollup ID, but no matching rollup blob was found; dropping it", ); } else { @@ -99,7 +99,7 @@ pub(super) fn reconstruct_blocks_from_verified_blobs( } fn remove_header_blob_matching_rollup_blob( - headers: &mut HashMap<[u8; 32], SubmittedMetadata>, + headers: &mut HashMap, rollup: &SubmittedRollupData, ) -> Option { // chaining methods and returning () to use the ? operator and to not bind the value diff --git a/crates/astria-conductor/src/celestia/reporting.rs b/crates/astria-conductor/src/celestia/reporting.rs index cd16454b6f..284bc4ac6c 100644 --- a/crates/astria-conductor/src/celestia/reporting.rs +++ b/crates/astria-conductor/src/celestia/reporting.rs @@ -53,7 +53,8 @@ impl<'a> Serialize for ReportReconstructedBlock<'a> { ]; let mut state = serializer.serialize_struct("ReconstructedBlockInfo", FIELDS.len())?; state.serialize_field(FIELDS[0], &self.0.celestia_height)?; - state.serialize_field(FIELDS[1], &base64(&self.0.block_hash))?; + // TODO: use the block hash's Display impl for this + state.serialize_field(FIELDS[1], &base64(&*self.0.block_hash))?; state.serialize_field(FIELDS[2], &self.0.transactions.len())?; state.serialize_field(FIELDS[3], &self.0.celestia_height)?; state.end() diff --git a/crates/astria-conductor/src/celestia/verify.rs b/crates/astria-conductor/src/celestia/verify.rs index 3ac09fa9d1..973a0f007f 100644 --- a/crates/astria-conductor/src/celestia/verify.rs +++ b/crates/astria-conductor/src/celestia/verify.rs @@ -5,6 +5,7 @@ use std::{ }; use astria_core::sequencerblock::v1::{ + block::BlockHash, SubmittedMetadata, SubmittedRollupData, }; @@ -25,7 +26,6 @@ use sequencer_client::{ Client as _, HttpClient as SequencerClient, }; -use telemetry::display::base64; use tokio_util::task::JoinMap; use tower::{ util::BoxService, @@ -58,7 +58,7 @@ use crate::executor::{ pub(super) struct VerifiedBlobs { celestia_height: u64, - header_blobs: HashMap<[u8; 32], SubmittedMetadata>, + header_blobs: HashMap, rollup_blobs: Vec, } @@ -75,7 +75,7 @@ impl VerifiedBlobs { self, ) -> ( u64, - HashMap<[u8; 32], SubmittedMetadata>, + HashMap, Vec, ) { (self.celestia_height, self.header_blobs, self.rollup_blobs) @@ -88,7 +88,7 @@ impl VerifiedBlobs { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct VerificationTaskKey { index: usize, - block_hash: [u8; 32], + block_hash: BlockHash, sequencer_height: SequencerHeight, } @@ -142,7 +142,7 @@ pub(super) async fn verify_metadata( .get(dropped_entry.block_hash()) .expect("must exist; just inserted an item under the same key"); info!( - block_hash = %base64(&dropped_entry.block_hash()), + block_hash = %dropped_entry.block_hash(), dropped_blob.sequencer_height = dropped_entry.height().value(), accepted_blob.sequencer_height = accepted_entry.height().value(), "two Sequencer header blobs were well formed and validated against \ @@ -154,7 +154,7 @@ pub(super) async fn verify_metadata( Ok(None) => {} Err(error) => { info!( - block_hash = %base64(&key.block_hash), + block_hash = %key.block_hash, sequencer_height = %key.sequencer_height, %error, "verification of sequencer blob was cancelled abruptly; dropping it" @@ -552,10 +552,13 @@ fn ensure_chain_ids_match(in_commit: &str, in_header: &str) -> eyre::Result<()> Ok(()) } -fn ensure_block_hashes_match(in_commit: &[u8], in_header: &[u8]) -> eyre::Result<()> { +fn ensure_block_hashes_match(in_commit: &[u8], in_header: &BlockHash) -> eyre::Result<()> { use base64::prelude::*; + // NOTE: we still re-encode the block hash using base64 instead of its display impl + // to ensure that the formatting of the two byte slices doesn't accidentally go + // out of whack should the display impl change. ensure!( - in_commit == in_header, + in_commit == in_header.as_ref(), "expected block hash `{}` (from commit), but found `{}` in retrieved metadata", BASE64_STANDARD.encode(in_commit), BASE64_STANDARD.encode(in_header), diff --git a/crates/astria-conductor/src/executor/mod.rs b/crates/astria-conductor/src/executor/mod.rs index 8c3155ca8b..3d1a32d30c 100644 --- a/crates/astria-conductor/src/executor/mod.rs +++ b/crates/astria-conductor/src/executor/mod.rs @@ -7,6 +7,7 @@ use astria_core::{ }, primitive::v1::RollupId, sequencerblock::v1::block::{ + BlockHash, FilteredSequencerBlock, FilteredSequencerBlockParts, }, @@ -285,7 +286,7 @@ impl Executor { { debug_span!("conductor::Executor::run_until_stopped").in_scope(||debug!( block.height = %block.sequencer_height(), - block.hash = %telemetry::display::base64(&block.block_hash), + block.hash = %block.block_hash(), "received block from celestia reader", )); if let Err(error) = self.execute_firm(block).await { @@ -298,7 +299,7 @@ impl Executor { { debug_span!("conductor::Executor::run_until_stopped").in_scope(||debug!( block.height = %block.height(), - block.hash = %telemetry::display::base64(&block.block_hash()), + block.hash = %block.block_hash(), "received block from sequencer reader", )); if let Err(error) = self.execute_soft(block).await { @@ -388,7 +389,7 @@ impl Executor { } #[instrument(skip_all, fields( - block.hash = %telemetry::display::base64(&block.block_hash()), + block.hash = %block.block_hash(), block.height = block.height().value(), err, ))] @@ -452,7 +453,7 @@ impl Executor { } #[instrument(skip_all, fields( - block.hash = %telemetry::display::base64(&block.block_hash), + block.hash = %block.block_hash(), block.height = block.sequencer_height().value(), err, ))] @@ -532,7 +533,7 @@ impl Executor { /// This function is called via [`Executor::execute_firm`] or [`Executor::execute_soft`], /// and should not be called directly. #[instrument(skip_all, fields( - block.hash = %telemetry::display::base64(&block.hash), + block.hash = %block.hash, block.height = block.height.value(), block.num_of_transactions = block.transactions.len(), rollup.parent_hash = %telemetry::display::base64(&parent_hash), @@ -690,7 +691,7 @@ enum Update { #[derive(Debug)] struct ExecutableBlock { - hash: [u8; 32], + hash: BlockHash, height: SequencerHeight, timestamp: pbjson_types::Timestamp, transactions: Vec, diff --git a/crates/astria-conductor/tests/blackbox/helpers/mod.rs b/crates/astria-conductor/tests/blackbox/helpers/mod.rs index da7dad5061..0cd9132e21 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mod.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mod.rs @@ -642,7 +642,7 @@ pub fn make_commit(height: u32) -> tendermint::block::Commit { height: height.into(), round: 0u16.into(), block_id: Some(tendermint::block::Id { - hash: tendermint::Hash::Sha256(block_hash), + hash: tendermint::Hash::Sha256(block_hash.get()), part_set_header: tendermint::block::parts::Header::default(), }), timestamp: Some(timestamp), @@ -657,7 +657,7 @@ pub fn make_commit(height: u32) -> tendermint::block::Commit { height: height.into(), round: 0u16.into(), block_id: tendermint::block::Id { - hash: tendermint::Hash::Sha256(block_hash), + hash: tendermint::Hash::Sha256(block_hash.get()), part_set_header: tendermint::block::parts::Header::default(), }, signatures: vec![tendermint::block::CommitSig::BlockIdFlagCommit { diff --git a/crates/astria-core/src/sequencerblock/v1/block.rs b/crates/astria-core/src/sequencerblock/v1/block.rs index 6b657b72b6..917bca21cd 100644 --- a/crates/astria-core/src/sequencerblock/v1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1/block.rs @@ -1,5 +1,7 @@ use std::{ collections::HashMap, + fmt::Display, + ops::Deref, vec::IntoIter, }; @@ -580,13 +582,70 @@ enum SequencerBlockHeaderErrorKind { /// Exists to provide convenient access to fields of a [`SequencerBlock`]. #[derive(Clone, Debug, PartialEq)] pub struct SequencerBlockParts { - pub block_hash: [u8; 32], + pub block_hash: BlockHash, pub header: SequencerBlockHeader, pub rollup_transactions: IndexMap, pub rollup_transactions_proof: merkle::Proof, pub rollup_ids_proof: merkle::Proof, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BlockHash([u8; 32]); + +impl BlockHash { + pub fn new(inner: [u8; 32]) -> Self { + Self(inner) + } + + pub fn get(&self) -> [u8; 32] { + self.0 + } +} + +impl AsRef<[u8]> for BlockHash { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for BlockHash { + type Target = [u8; 32]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, thiserror::Error)] +#[error("block hash requires 32 bytes, but slice contained `{actual}`")] +pub struct BlockHashFromSliceError { + actual: usize, + source: std::array::TryFromSliceError, +} + +impl<'a> TryFrom<&'a [u8]> for BlockHash { + type Error = BlockHashFromSliceError; + + fn try_from(value: &'a [u8]) -> Result { + let inner = value.try_into().map_err(|source| Self::Error { + actual: value.len(), + source, + })?; + Ok(Self(inner)) + } +} + +// The display impl follows that of the pbjson derivation. +impl Display for BlockHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use base64::{ + display::Base64Display, + engine::general_purpose::STANDARD, + }; + Base64Display::new(&self.0, &STANDARD).fmt(f) + } +} + /// `SequencerBlock` is constructed from a tendermint/cometbft block by /// converting its opaque `data` bytes into sequencer specific types. #[derive(Clone, Debug, PartialEq)] @@ -597,7 +656,7 @@ pub struct SequencerBlockParts { pub struct SequencerBlock { /// The result of hashing the cometbft header. Guaranteed to not be `None` as compared to /// the cometbft/tendermint-rs return type. - block_hash: [u8; 32], + block_hash: BlockHash, /// the block header, which contains the cometbft header and additional sequencer-specific /// commitments. header: SequencerBlockHeader, @@ -622,7 +681,7 @@ impl SequencerBlock { /// /// This is done by hashing the `CometBFT` header stored in this block. #[must_use] - pub fn block_hash(&self) -> &[u8; 32] { + pub fn block_hash(&self) -> &BlockHash { &self.block_hash } @@ -687,7 +746,7 @@ impl SequencerBlock { rollup_ids_proof, } = self; raw::SequencerBlock { - block_hash: Bytes::copy_from_slice(&block_hash), + block_hash: Bytes::copy_from_slice(block_hash.as_ref()), header: Some(header.into_raw()), rollup_transactions: rollup_transactions .into_values() @@ -862,7 +921,7 @@ impl SequencerBlock { ); Ok(Self { - block_hash, + block_hash: BlockHash(block_hash), header: SequencerBlockHeader { chain_id, height, @@ -1031,7 +1090,7 @@ where /// Exists to provide convenient access to fields of a [`FilteredSequencerBlock`]. #[derive(Debug, Clone, PartialEq)] pub struct FilteredSequencerBlockParts { - pub block_hash: [u8; 32], + pub block_hash: BlockHash, pub header: SequencerBlockHeader, // filtered set of rollup transactions pub rollup_transactions: IndexMap, @@ -1049,7 +1108,7 @@ pub struct FilteredSequencerBlockParts { reason = "we want consistent and specific naming" )] pub struct FilteredSequencerBlock { - block_hash: [u8; 32], + block_hash: BlockHash, header: SequencerBlockHeader, // filtered set of rollup transactions rollup_transactions: IndexMap, @@ -1063,7 +1122,7 @@ pub struct FilteredSequencerBlock { impl FilteredSequencerBlock { #[must_use] - pub fn block_hash(&self) -> &[u8; 32] { + pub fn block_hash(&self) -> &BlockHash { &self.block_hash } @@ -1113,7 +1172,7 @@ impl FilteredSequencerBlock { .. } = self; raw::FilteredSequencerBlock { - block_hash: Bytes::copy_from_slice(&block_hash), + block_hash: Bytes::copy_from_slice(&*block_hash), header: Some(header.into_raw()), rollup_transactions: rollup_transactions .into_values() diff --git a/crates/astria-core/src/sequencerblock/v1/celestia.rs b/crates/astria-core/src/sequencerblock/v1/celestia.rs index 1fb359d0a4..a89e41c31c 100644 --- a/crates/astria-core/src/sequencerblock/v1/celestia.rs +++ b/crates/astria-core/src/sequencerblock/v1/celestia.rs @@ -6,6 +6,7 @@ use sha2::{ use super::{ block::{ + BlockHash, RollupTransactionsParts, SequencerBlock, SequencerBlockHeader, @@ -134,7 +135,7 @@ enum SubmittedRollupDataErrorKind { /// they can be converted directly into one another. This can change in the future. pub struct UncheckedSubmittedRollupData { /// The hash of the sequencer block. Must be 32 bytes. - pub sequencer_block_hash: [u8; 32], + pub sequencer_block_hash: BlockHash, /// The 32 bytes identifying the rollup this blob belongs to. Matches /// `astria.sequencerblock.v1.RollupTransactions.rollup_id` pub rollup_id: RollupId, @@ -154,7 +155,7 @@ impl UncheckedSubmittedRollupData { #[derive(Clone, Debug)] pub struct SubmittedRollupData { /// The hash of the sequencer block. Must be 32 bytes. - sequencer_block_hash: [u8; 32], + sequencer_block_hash: BlockHash, /// The 32 bytes identifying the rollup this blob belongs to. Matches /// `astria.sequencerblock.v1.RollupTransactions.rollup_id` rollup_id: RollupId, @@ -181,7 +182,7 @@ impl SubmittedRollupData { } #[must_use] - pub fn sequencer_block_hash(&self) -> &[u8; 32] { + pub fn sequencer_block_hash(&self) -> &BlockHash { &self.sequencer_block_hash } @@ -235,7 +236,7 @@ impl SubmittedRollupData { proof, } = self; raw::SubmittedRollupData { - sequencer_block_hash: Bytes::copy_from_slice(&sequencer_block_hash), + sequencer_block_hash: Bytes::copy_from_slice(&*sequencer_block_hash), rollup_id: Some(rollup_id.to_raw()), transactions, proof: Some(proof.into_raw()), @@ -382,7 +383,7 @@ enum SubmittedMetadataErrorKind { /// access the sequencer block's internal types. #[derive(Clone, Debug)] pub struct UncheckedSubmittedMetadata { - pub block_hash: [u8; 32], + pub block_hash: BlockHash, /// The original `CometBFT` header that is the input to this blob's original sequencer block. /// Corresponds to `astria.SequencerBlock.header`. pub header: SequencerBlockHeader, @@ -474,7 +475,7 @@ impl UncheckedSubmittedMetadata { #[derive(Clone, Debug)] pub struct SubmittedMetadata { /// The block hash obtained from hashing `.header`. - block_hash: [u8; 32], + block_hash: BlockHash, /// The sequencer block header. header: SequencerBlockHeader, /// The rollup IDs for which `SubmittedRollupData`s were submitted to celestia. @@ -507,7 +508,7 @@ impl<'a> Iterator for RollupIdIter<'a> { impl SubmittedMetadata { /// Returns the block hash of the tendermint header stored in this blob. #[must_use] - pub fn block_hash(&self) -> &[u8; 32] { + pub fn block_hash(&self) -> &BlockHash { &self.block_hash } @@ -617,7 +618,7 @@ impl SubmittedMetadata { .. } = self; raw::SubmittedMetadata { - block_hash: Bytes::copy_from_slice(&block_hash), + block_hash: Bytes::copy_from_slice(&*block_hash), header: Some(header.into_raw()), rollup_ids: rollup_ids.into_iter().map(RollupId::into_raw).collect(), rollup_transactions_proof: Some(rollup_transactions_proof.into_raw()), diff --git a/crates/astria-sequencer/src/grpc/sequencer.rs b/crates/astria-sequencer/src/grpc/sequencer.rs index 7d74f077f6..813180fe4b 100644 --- a/crates/astria-sequencer/src/grpc/sequencer.rs +++ b/crates/astria-sequencer/src/grpc/sequencer.rs @@ -160,7 +160,7 @@ impl SequencerService for SequencerServer { let all_rollup_ids = all_rollup_ids.into_iter().map(RollupId::into_raw).collect(); let block = RawFilteredSequencerBlock { - block_hash: Bytes::copy_from_slice(&block_hash), + block_hash: Bytes::copy_from_slice(&*block_hash), header: Some(header.into_raw()), rollup_transactions, rollup_transactions_proof: Some(rollup_transactions_proof.into_raw()), diff --git a/crates/astria-sequencer/src/grpc/state_ext.rs b/crates/astria-sequencer/src/grpc/state_ext.rs index fc377d48c1..4bf1d6cdbf 100644 --- a/crates/astria-sequencer/src/grpc/state_ext.rs +++ b/crates/astria-sequencer/src/grpc/state_ext.rs @@ -1,6 +1,7 @@ use astria_core::{ primitive::v1::RollupId, sequencerblock::v1::block::{ + BlockHash, RollupTransactions, SequencerBlock, SequencerBlockHeader, @@ -31,7 +32,7 @@ use crate::storage::StoredValue; #[async_trait] pub(crate) trait StateReadExt: StateRead { #[instrument(skip_all)] - async fn get_block_hash_by_height(&self, height: u64) -> Result<[u8; 32]> { + async fn get_block_hash_by_height(&self, height: u64) -> Result { let Some(bytes) = self .nonverifiable_get_raw(keys::block_hash_by_height(height).as_bytes()) .await @@ -41,14 +42,14 @@ pub(crate) trait StateReadExt: StateRead { bail!("block hash not found for given height"); }; StoredValue::deserialize(&bytes) - .and_then(|value| storage::BlockHash::try_from(value).map(<[u8; 32]>::from)) + .and_then(|value| storage::BlockHash::try_from(value).map(BlockHash::from)) .wrap_err("invalid block hash bytes") } #[instrument(skip_all)] async fn get_sequencer_block_header_by_hash( &self, - hash: &[u8; 32], + hash: &BlockHash, ) -> Result { let Some(bytes) = self .nonverifiable_get_raw(keys::sequencer_block_header_by_hash(hash).as_bytes()) @@ -66,7 +67,7 @@ pub(crate) trait StateReadExt: StateRead { } #[instrument(skip_all)] - async fn get_rollup_ids_by_block_hash(&self, hash: &[u8; 32]) -> Result> { + async fn get_rollup_ids_by_block_hash(&self, hash: &BlockHash) -> Result> { let Some(bytes) = self .nonverifiable_get_raw(keys::rollup_ids_by_hash(hash).as_bytes()) .await @@ -183,7 +184,7 @@ pub(crate) trait StateWriteExt: StateWrite { #[instrument(skip_all)] async fn get_sequencer_block_by_hash( state: &S, - hash: &[u8; 32], + hash: &BlockHash, ) -> Result { let header = state .get_sequencer_block_header_by_hash(hash) @@ -228,7 +229,7 @@ async fn get_sequencer_block_by_hash( fn put_block_hash( state: &mut S, block_height: tendermint::block::Height, - block_hash: [u8; 32], + block_hash: BlockHash, ) -> Result<()> { let bytes = StoredValue::from(storage::BlockHash::from(&block_hash)) .serialize() diff --git a/crates/astria-sequencer/src/grpc/storage/values/block_hash.rs b/crates/astria-sequencer/src/grpc/storage/values/block_hash.rs index 4c772091ca..c96d1bfb66 100644 --- a/crates/astria-sequencer/src/grpc/storage/values/block_hash.rs +++ b/crates/astria-sequencer/src/grpc/storage/values/block_hash.rs @@ -28,15 +28,15 @@ impl<'a> Debug for BlockHash<'a> { } } -impl<'a> From<&'a [u8; 32]> for BlockHash<'a> { - fn from(block_hash: &'a [u8; 32]) -> Self { +impl<'a> From<&'a astria_core::sequencerblock::v1::block::BlockHash> for BlockHash<'a> { + fn from(block_hash: &'a astria_core::sequencerblock::v1::block::BlockHash) -> Self { BlockHash(Cow::Borrowed(block_hash)) } } -impl<'a> From> for [u8; 32] { +impl<'a> From> for astria_core::sequencerblock::v1::block::BlockHash { fn from(block_hash: BlockHash<'a>) -> Self { - block_hash.0.into_owned() + Self::new(block_hash.0.into_owned()) } } From 752fc2016d46a5f3947ede1a5015f9da46754848 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Fri, 29 Nov 2024 20:37:28 +0100 Subject: [PATCH 45/73] update auctioneer to make use of the block hash type --- .../src/auctioneer/inner/auction/factory.rs | 2 +- .../src/auctioneer/inner/auction/mod.rs | 13 ++++++++----- crates/astria-auctioneer/src/block/mod.rs | 19 +++++++++---------- crates/astria-auctioneer/src/bundle/mod.rs | 13 ++++++------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index acef8ad22e..18f87402f8 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -38,7 +38,7 @@ impl Factory { &mut self, block: FilteredSequencerBlock, ) -> Running { - let new_auction_id = super::Id::from_sequencer_block_hash(*block.block_hash()); + let new_auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); let height = block.height().into(); let (handle, auction) = super::Builder { diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index fee42460d3..8574713066 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -40,9 +40,12 @@ use std::time::Duration; use allocation_rule::FirstPrice; -use astria_core::primitive::v1::{ - asset, - RollupId, +use astria_core::{ + primitive::v1::{ + asset, + RollupId, + }, + sequencerblock::v1::block::BlockHash, }; use astria_eyre::eyre::{ self, @@ -80,8 +83,8 @@ pub(super) use running::Running; struct Id([u8; 32]); impl Id { - pub(super) fn from_sequencer_block_hash(block_hash: [u8; 32]) -> Self { - Self(block_hash) + pub(super) fn from_sequencer_block_hash(block_hash: &BlockHash) -> Self { + Self(block_hash.get()) } } diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index 0cb79a8fd8..ff2c4f092e 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -4,14 +4,13 @@ use astria_core::{ bundle::v1alpha1 as raw_bundle, sequencerblock::{ optimisticblock::v1alpha1 as raw_optimistic_block, - v1::{ - self as raw_sequencer_block, - }, + v1 as raw_sequencer_block, }, }, primitive::v1::RollupId, sequencerblock::v1::{ block::{ + BlockHash, FilteredSequencerBlock, FilteredSequencerBlockParts, }, @@ -89,7 +88,7 @@ impl Optimistic { let timestamp = Some(convert_tendermint_time_to_protobuf_timestamp(header.time())); Ok(raw_bundle::BaseBlock { - sequencer_block_hash: Bytes::copy_from_slice(&block_hash), + sequencer_block_hash: Bytes::copy_from_slice(&*block_hash), transactions, timestamp, }) @@ -101,7 +100,7 @@ pub(crate) struct Executed { /// The rollup block metadata that resulted from executing the optimistic block. block: execution::v1::Block, /// The hash of the sequencer block that was executed optimistically. - sequencer_block_hash: [u8; 32], + sequencer_block_hash: BlockHash, } impl Executed { @@ -126,8 +125,8 @@ impl Executed { }) } - pub(crate) fn sequencer_block_hash(&self) -> [u8; 32] { - self.sequencer_block_hash + pub(crate) fn sequencer_block_hash(&self) -> &BlockHash { + &self.sequencer_block_hash } pub(crate) fn parent_rollup_block_hash(&self) -> [u8; 32] { @@ -146,7 +145,7 @@ pub(crate) struct Commitment { /// The height of the sequencer block that was committed. sequencer_height: u64, /// The hash of the sequencer block that was committed. - sequnecer_block_hash: [u8; 32], + sequnecer_block_hash: BlockHash, } impl Commitment { @@ -163,8 +162,8 @@ impl Commitment { }) } - pub(crate) fn sequencer_block_hash(&self) -> [u8; 32] { - self.sequnecer_block_hash + pub(crate) fn sequencer_block_hash(&self) -> &BlockHash { + &self.sequnecer_block_hash } /// The height of the sequencer block that was committed. diff --git a/crates/astria-auctioneer/src/bundle/mod.rs b/crates/astria-auctioneer/src/bundle/mod.rs index 748bb55d14..5c3dbdd032 100644 --- a/crates/astria-auctioneer/src/bundle/mod.rs +++ b/crates/astria-auctioneer/src/bundle/mod.rs @@ -3,9 +3,7 @@ use astria_core::{ Signature, VerificationKey, }, - generated::bundle::v1alpha1::{ - self as raw, - }, + generated::bundle::v1alpha1 as raw, primitive::v1::{ asset, RollupId, @@ -14,6 +12,7 @@ use astria_core::{ action::RollupDataSubmission, TransactionBody, }, + sequencerblock::v1::block::BlockHash, }; use astria_eyre::eyre::{ self, @@ -36,7 +35,7 @@ pub(crate) struct Bundle { prev_rollup_block_hash: [u8; 32], /// The hash of the sequencer block used to derive the rollup block that this bundle is based /// on. - base_sequencer_block_hash: [u8; 32], + base_sequencer_block_hash: BlockHash, } impl Bundle { @@ -65,7 +64,7 @@ impl Bundle { raw::Bundle { fee: self.fee, transactions: self.transactions, - base_sequencer_block_hash: Bytes::copy_from_slice(&self.base_sequencer_block_hash), + base_sequencer_block_hash: Bytes::copy_from_slice(&*self.base_sequencer_block_hash), prev_rollup_block_hash: Bytes::copy_from_slice(&self.prev_rollup_block_hash), } } @@ -104,8 +103,8 @@ impl Bundle { self.prev_rollup_block_hash } - pub(crate) fn base_sequencer_block_hash(&self) -> [u8; 32] { - self.base_sequencer_block_hash + pub(crate) fn base_sequencer_block_hash(&self) -> &BlockHash { + &self.base_sequencer_block_hash } } From 138737729ae67970bf55f3116a9dec32d5e9c6a6 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 12:15:11 +0100 Subject: [PATCH 46/73] re-instrument all handlers in primary auctioneer event loop --- .../src/auctioneer/inner/auction/mod.rs | 2 +- .../src/auctioneer/inner/auction/running.rs | 8 +- .../src/auctioneer/inner/mod.rs | 84 +++++++++++++------ 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 8574713066..ac38e54d46 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -80,7 +80,7 @@ pub(super) use factory::Factory; pub(super) use running::Running; #[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] -struct Id([u8; 32]); +pub(super) struct Id([u8; 32]); impl Id { pub(super) fn from_sequencer_block_hash(block_hash: &BlockHash) -> Self { diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs index 72f680d334..1015f9ba04 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -33,6 +33,10 @@ impl Running { self.task.abort(); } + pub(in crate::auctioneer::inner) fn id(&self) -> &super::Id { + &self.id + } + #[instrument(skip(self))] // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> // eyre::Result<()> { @@ -143,13 +147,13 @@ impl Running { } impl Future for Running { - type Output = eyre::Result<()>; + type Output = (super::Id, eyre::Result<()>); fn poll( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { let res = std::task::ready!(self.task.poll_unpin(cx)); - std::task::Poll::Ready(flatten_join_result(res)) + std::task::Poll::Ready((self.id, flatten_join_result(res))) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 28ce8d237a..dd686ff09a 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -24,10 +24,12 @@ use tokio::{ use tokio_util::sync::CancellationToken; use tracing::{ error, + field, info, info_span, instrument, warn, + Span, }; use crate::{ @@ -165,8 +167,8 @@ impl Inner { let _ = self.handle_executed_block(res); } - res = async { self.running_auction.as_mut().unwrap().await }, if self.running_auction.is_some() => { - let _ = self.handle_auction_result(res); + (id, res) = async { self.running_auction.as_mut().unwrap().await }, if self.running_auction.is_some() => { + let _ = self.handle_auction_result(id, res); } Some(res) = self.bundles.next() => { @@ -183,13 +185,20 @@ impl Inner { Ok(()) } - #[instrument(skip_all, err)] - fn handle_auction_result(&self, res: eyre::Result<()>) -> eyre::Result<()> { + /// Handles the result of an auction. + /// + /// This method only exists to emit the auction result (only error right now) under a right + /// span. + #[instrument(skip_all, fields(%auction_id), err)] + fn handle_auction_result( + &self, + auction_id: auction::Id, + res: eyre::Result<()>, + ) -> eyre::Result<()> { res } - // #[instrument(skip(self), fields(auction.old_id = - // %base64(self.current_block.sequencer_block_hash())), err)] + #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_optimistic_block( &mut self, optimistic_block: eyre::Result, @@ -197,25 +206,21 @@ impl Inner { let optimistic_block = optimistic_block.wrap_err("encountered problem receiving optimistic block message")?; + Span::current().record("block_hash", field::display(optimistic_block.block_hash())); + // FIXME: Don't clone this; find a better way. let new_auction = self.auction_factory.start_new(optimistic_block.clone()); + info!(auction_id = %new_auction.id(), "started new auction"); + if let Some(old_auction) = self.running_auction.replace(new_auction) { // NOTE: We just throw away the old auction after aborting it. Is there // value in `.join`ing it after the abort except for ensuring that it - // did indeed abort? What if the running auction is not tracked inside - // the "auction manager", but the auction manager is turned into a simpler - // factory so that the running auction is running inside the auctioneer? - // Then the auctioneer would always have the previous/old auction and could - // decide what to do with it. That might make this a cleaner implementation. - // let old_auction_id = running_auction.id; - // info!( - // %new_auction_id, - // %old_auction_id, - // "received optimistic block, aborting old auction and starting new auction" - // ); + // did indeed abort? old_auction.abort(); + info!(auction_id = %old_auction.id(), "terminated old auction"); } + // TODO: do conversion && sending in one operation let base_block = crate::block::Optimistic::new(optimistic_block) .try_into_base_block(self.rollup_id) // FIXME: give this their proper wire names @@ -227,52 +232,79 @@ impl Inner { Ok(()) } - // #[instrument(skip_all, fields(auction.id = - // %base64(self.current_block.sequencer_block_hash())), err)] + #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_block_commitment( &mut self, block_commitment: eyre::Result, ) -> eyre::Result<()> { let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; + Span::current().record( + "block_hash", + field::display(block_commitment.sequencer_block_hash()), + ); if let Some(running_auction) = &mut self.running_auction { running_auction .start_timer(block_commitment) .wrap_err("failed to start timer")?; + info!(auction_id = %running_auction.id(), "started auction timer"); } else { - // TODO: emit an event? + info!( + "received a block commitment but did not start auction timer because no auction \ + was running" + ); } Ok(()) } - // #[instrument(skip_all, fields(auction.id = - // %base64(self.current_block.sequencer_block_hash())))] + #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_executed_block( &mut self, executed_block: eyre::Result, ) -> eyre::Result<()> { let executed_block = executed_block.wrap_err("failed to receive executed block")?; + Span::current().record( + "block_hash", + field::display(executed_block.sequencer_block_hash()), + ); if let Some(running_auction) = &mut self.running_auction { running_auction .start_processing_bids(executed_block) .wrap_err("failed to start processing bids")?; + info!( + auction_id = %running_auction.id(), + "set auction to start processing bids based on executed block", + ); } else { - // TODO: emit an event? + info!( + "received an executed block but did not set auction to start processing bids \ + because no auction was running" + ); } Ok(()) } - // #[instrument(skip_all, fields(auction.id = - // %base64(self.current_block.sequencer_block_hash())))] + #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { let bundle = bundle.wrap_err("received problematic bundle")?; + Span::current().record( + "block_hash", + field::display(bundle.base_sequencer_block_hash()), + ); if let Some(running_auction) = &mut self.running_auction { running_auction .forward_bundle_to_auction(bundle) .wrap_err("failed to forward bundle to auction")?; + info!( + auction_id = %running_auction.id(), + "forwarded bundle to auction" + ) } else { - // TODO: emit an event? + info!( + "received a bundle but did not forward it to the auction because no auction was \ + running", + ); } Ok(()) } From c667efc44c3d94e659f87520020d00bd48b2a7a6 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:13:21 +0100 Subject: [PATCH 47/73] track result of cancelled auctions --- .../src/auctioneer/inner/mod.rs | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index dd686ff09a..9cbf03621f 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -14,6 +14,7 @@ use astria_eyre::eyre::{ WrapErr as _, }; use futures::{ + stream::FuturesUnordered, Future, StreamExt as _, }; @@ -29,6 +30,7 @@ use tracing::{ info_span, instrument, warn, + Level, Span, }; @@ -54,6 +56,7 @@ pub(super) struct Inner { auction_factory: auction::Factory, block_commitments: BlockCommitmentStream, bundles: BundleStream, + cancelled_auctions: FuturesUnordered, executed_blocks: ExecuteOptimisticBlockStream, running_auction: Option, optimistic_blocks: OptimisticBlockStream, @@ -116,6 +119,7 @@ impl Inner { auction_factory, block_commitments: sequencer_channel.open_get_block_commitment_stream(), bundles: rollup_channel.open_bundle_stream(), + cancelled_auctions: FuturesUnordered::new(), executed_blocks: rollup_channel.open_execute_optimistic_block_stream(), optimistic_blocks: sequencer_channel.open_get_optimistic_block_stream(rollup_id), rollup_id, @@ -168,7 +172,7 @@ impl Inner { } (id, res) = async { self.running_auction.as_mut().unwrap().await }, if self.running_auction.is_some() => { - let _ = self.handle_auction_result(id, res); + let _ = self.handle_completed_auction(id, res); } Some(res) = self.bundles.next() => { @@ -181,16 +185,31 @@ impl Inner { Err(err) => return Err(err).wrap_err("pending nonce publisher task panicked"), } } + + Some((id, res)) = self.cancelled_auctions.next() => { + let _ = self.handle_cancelled_auction(id, res); + } ); Ok(()) } - /// Handles the result of an auction. + /// Handles the result of an auction running to completion. /// - /// This method only exists to emit the auction result (only error right now) under a right - /// span. + /// This method only exists to emit the auction result (only error right now) under a span. #[instrument(skip_all, fields(%auction_id), err)] - fn handle_auction_result( + fn handle_completed_auction( + &self, + auction_id: auction::Id, + res: eyre::Result<()>, + ) -> eyre::Result<()> { + res + } + + /// Handles the result of cancelled auctions. + /// + /// This method only exists to emit the auction result (only error right now) under a span. + #[instrument(skip_all, fields(%auction_id), err(level = Level::INFO))] + fn handle_cancelled_auction( &self, auction_id: auction::Id, res: eyre::Result<()>, @@ -213,11 +232,9 @@ impl Inner { info!(auction_id = %new_auction.id(), "started new auction"); if let Some(old_auction) = self.running_auction.replace(new_auction) { - // NOTE: We just throw away the old auction after aborting it. Is there - // value in `.join`ing it after the abort except for ensuring that it - // did indeed abort? old_auction.abort(); - info!(auction_id = %old_auction.id(), "terminated old auction"); + info!(auction_id = %old_auction.id(), "cancelled old auction"); + self.cancelled_auctions.push(old_auction); } // TODO: do conversion && sending in one operation From 8ef55fc4cf759a2f645faf018a738662b46e84e8 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:19:29 +0100 Subject: [PATCH 48/73] starting new auctions doesn't need cloned blocks --- .../astria-auctioneer/src/auctioneer/inner/auction/factory.rs | 2 +- crates/astria-auctioneer/src/auctioneer/inner/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 18f87402f8..352b882661 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -36,7 +36,7 @@ impl Factory { #[instrument(skip(self))] pub(in crate::auctioneer::inner) fn start_new( &mut self, - block: FilteredSequencerBlock, + block: &FilteredSequencerBlock, ) -> Running { let new_auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); let height = block.height().into(); diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 9cbf03621f..5e1e39300d 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -227,8 +227,7 @@ impl Inner { Span::current().record("block_hash", field::display(optimistic_block.block_hash())); - // FIXME: Don't clone this; find a better way. - let new_auction = self.auction_factory.start_new(optimistic_block.clone()); + let new_auction = self.auction_factory.start_new(&optimistic_block); info!(auction_id = %new_auction.id(), "started new auction"); if let Some(old_auction) = self.running_auction.replace(new_auction) { From c33ba78630368cf4d8caec534abce5aed8f54509 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:20:24 +0100 Subject: [PATCH 49/73] auction factory does not need mutable access or instrumentation --- .../src/auctioneer/inner/auction/factory.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 352b882661..de7383136c 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -7,10 +7,6 @@ use astria_core::{ }, sequencerblock::v1::block::FilteredSequencerBlock, }; -use tracing::{ - instrument, - warn, -}; use super::{ PendingNonceSubscriber, @@ -31,11 +27,8 @@ pub(in crate::auctioneer::inner) struct Factory { } impl Factory { - // pub(in crate::auctioneer::inner) fn new_auction(&mut self, auction_id: Id) { - // TODO: Add some better instrumentation. - #[instrument(skip(self))] pub(in crate::auctioneer::inner) fn start_new( - &mut self, + &self, block: &FilteredSequencerBlock, ) -> Running { let new_auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); From d4b29fc7a283a7fd3beb243154ac03ed772b790b Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:35:43 +0100 Subject: [PATCH 50/73] flatten all auction construction directly into the factory Also removes auction Handle and Builder - they add unnecessary complexity. --- .../src/auctioneer/inner/auction/builder.rs | 73 ------------------- .../src/auctioneer/inner/auction/factory.rs | 22 ++++-- .../src/auctioneer/inner/auction/mod.rs | 36 +-------- .../src/auctioneer/inner/auction/running.rs | 21 ++++-- 4 files changed, 30 insertions(+), 122 deletions(-) delete mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs deleted file mode 100644 index f4e3c18134..0000000000 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/builder.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::time::Duration; - -use astria_core::primitive::v1::{ - asset, - RollupId, -}; -use tokio::sync::mpsc; - -use super::{ - Auction, - Handle, - Id, - PendingNonceSubscriber, - SequencerKey, -}; -pub(in crate::auctioneer) struct Builder { - /// The endpoint for the sequencer ABCI service used to submit transactions - pub(super) sequencer_abci_client: sequencer_client::HttpClient, - /// The amount of time to wait after a commit before closing the auction for bids and - /// submitting the resulting transaction - pub(super) latency_margin: Duration, - /// The ID of the auction to be run - pub(super) auction_id: Id, - /// The key used to sign sequencer transactions - pub(super) sequencer_key: SequencerKey, - /// The denomination of the fee asset used in the sequencer transactions - pub(super) fee_asset_denomination: asset::Denom, - /// The chain ID used for sequencer transactions - pub(super) sequencer_chain_id: String, - /// The rollup ID used for `RollupDataSubmission` with the auction result - pub(super) rollup_id: RollupId, - pub(super) pending_nonce: PendingNonceSubscriber, -} - -impl Builder { - pub(super) fn build(self) -> (Handle, Auction) { - let Self { - sequencer_abci_client, - latency_margin, - auction_id, - fee_asset_denomination, - rollup_id, - sequencer_key, - sequencer_chain_id, - pending_nonce, - } = self; - - // TODO: get the capacities from config or something instead of using a magic number - let (commands_tx, commands_rx) = mpsc::channel(16); - let (new_bundles_tx, new_bundles_rx) = mpsc::channel(16); - - let auction = Auction { - sequencer_abci_client, - commands_rx, - new_bundles_rx, - latency_margin, - id: auction_id, - sequencer_key, - fee_asset_denomination, - sequencer_chain_id, - rollup_id, - pending_nonce, - }; - - ( - Handle { - commands_tx, - new_bundles_tx, - }, - auction, - ) - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index de7383136c..cf13318d4e 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -7,8 +7,10 @@ use astria_core::{ }, sequencerblock::v1::block::FilteredSequencerBlock, }; +use tokio::sync::mpsc; use super::{ + Auction, PendingNonceSubscriber, Running, SequencerKey, @@ -31,26 +33,32 @@ impl Factory { &self, block: &FilteredSequencerBlock, ) -> Running { - let new_auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); + let auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); let height = block.height().into(); - let (handle, auction) = super::Builder { + // TODO: get the capacities from config or something instead of using a magic number + let (commands_tx, commands_rx) = mpsc::channel(16); + let (bundles_tx, bundles_rx) = mpsc::channel(16); + + let auction = Auction { sequencer_abci_client: self.sequencer_abci_client.clone(), + commands_rx, + bundles_rx, latency_margin: self.latency_margin, - auction_id: new_auction_id, + id: auction_id, sequencer_key: self.sequencer_key.clone(), fee_asset_denomination: self.fee_asset_denomination.clone(), sequencer_chain_id: self.sequencer_chain_id.clone(), rollup_id: self.rollup_id, pending_nonce: self.pending_nonce.clone(), - } - .build(); + }; Running { - id: new_auction_id, + id: auction_id, height, parent_block_of_executed: None, - sender: handle, + commands: commands_tx, + bundles: bundles_tx, task: tokio::task::spawn(auction.run()), } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index ac38e54d46..6d7c86a072 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -53,7 +53,6 @@ use astria_eyre::eyre::{ Context, OptionExt as _, }; -pub(super) use builder::Builder; use sequencer_client::SequencerClientExt; use tokio::{ select, @@ -73,7 +72,6 @@ use crate::{ }; mod allocation_rule; -mod builder; pub(super) mod factory; mod running; pub(super) use factory::Factory; @@ -103,43 +101,13 @@ enum Command { StartTimer, } -pub(super) struct Handle { - commands_tx: mpsc::Sender, - new_bundles_tx: mpsc::Sender, -} - -impl Handle { - pub(super) fn start_processing_bids(&mut self) -> eyre::Result<()> { - self.commands_tx - .try_send(Command::StartProcessingBids) - .wrap_err("unable to send command to start processing bids to auction")?; - Ok(()) - } - - pub(super) fn start_timer(&mut self) -> eyre::Result<()> { - self.commands_tx - .try_send(Command::StartTimer) - .wrap_err("unable to send command to start time to auction")?; - - Ok(()) - } - - pub(super) fn try_send_bundle(&mut self, bundle: Bundle) -> eyre::Result<()> { - self.new_bundles_tx - .try_send(bundle) - .wrap_err("bid channel full")?; - - Ok(()) - } -} - struct Auction { /// The sequencer's ABCI client, used for submitting transactions sequencer_abci_client: sequencer_client::HttpClient, /// Channel for receiving commands sent via the handle commands_rx: mpsc::Receiver, /// Channel for receiving new bundles - new_bundles_rx: mpsc::Receiver, + bundles_rx: mpsc::Receiver, /// The time between receiving a block commitment latency_margin: Duration, /// The ID of the auction @@ -191,7 +159,7 @@ impl Auction { } } - Some(bundle) = self.new_bundles_rx.recv(), if auction_is_open => { + Some(bundle) = self.bundles_rx.recv(), if auction_is_open => { if allocation_rule.bid(bundle.clone()) { info!( auction.id = %self.id, diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs index 1015f9ba04..69afc54447 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -6,13 +6,17 @@ use futures::{ Future, FutureExt, }; -use tokio::task::JoinHandle; +use tokio::{ + sync::mpsc, + task::JoinHandle, +}; use tracing::{ info, instrument, warn, }; +use super::Command; use crate::{ block::Commitment, bundle::Bundle, @@ -24,7 +28,8 @@ pub(in crate::auctioneer::inner) struct Running { pub(super) height: u64, pub(super) parent_block_of_executed: Option<[u8; 32]>, // TODO: Rename this to AuctionSender or smth like that - pub(super) sender: super::Handle, + pub(super) commands: mpsc::Sender, + pub(super) bundles: mpsc::Sender, pub(super) task: JoinHandle>, } @@ -48,8 +53,8 @@ impl Running { super::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); if self.id == id_according_to_block && block_commitment.sequencer_height() == self.height { - self.sender - .start_timer() + self.commands + .try_send(Command::StartTimer) .wrap_err("failed to send command to start timer to auction")?; } else { // TODO: provide better information on the blocks/currently running auction. @@ -83,8 +88,8 @@ impl Running { let _ = self .parent_block_of_executed .replace(block.parent_rollup_block_hash()); - self.sender - .start_processing_bids() + self.commands + .try_send(Command::StartProcessingBids) .wrap_err("failed to send command to start processing bids")?; } else { // TODO: bring back the fields to track the dropped block and current block @@ -125,8 +130,8 @@ impl Running { let ids_match = self.id == id_according_to_bundle; let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); if ids_match && parent_blocks_match { - self.sender - .try_send_bundle(bundle) + self.bundles + .try_send(bundle) .wrap_err("failed to add bundle to auction")?; } else { warn!( From 8d9f4909a9eaf5678b824e8846afc56938572129 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:40:14 +0100 Subject: [PATCH 51/73] rename Auction to Worker, Running to Auction --- .../src/auctioneer/inner/auction/factory.rs | 10 +++++----- .../src/auctioneer/inner/auction/mod.rs | 6 +++--- .../src/auctioneer/inner/auction/running.rs | 13 ++++++------- .../astria-auctioneer/src/auctioneer/inner/mod.rs | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index cf13318d4e..58c8b56df9 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -12,8 +12,8 @@ use tokio::sync::mpsc; use super::{ Auction, PendingNonceSubscriber, - Running, SequencerKey, + Worker, }; pub(in crate::auctioneer::inner) struct Factory { @@ -32,7 +32,7 @@ impl Factory { pub(in crate::auctioneer::inner) fn start_new( &self, block: &FilteredSequencerBlock, - ) -> Running { + ) -> Auction { let auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); let height = block.height().into(); @@ -40,7 +40,7 @@ impl Factory { let (commands_tx, commands_rx) = mpsc::channel(16); let (bundles_tx, bundles_rx) = mpsc::channel(16); - let auction = Auction { + let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), commands_rx, bundles_rx, @@ -53,13 +53,13 @@ impl Factory { pending_nonce: self.pending_nonce.clone(), }; - Running { + Auction { id: auction_id, height, parent_block_of_executed: None, commands: commands_tx, bundles: bundles_tx, - task: tokio::task::spawn(auction.run()), + worker: tokio::task::spawn(auction.run()), } } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 6d7c86a072..5993e596d5 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -75,7 +75,7 @@ mod allocation_rule; pub(super) mod factory; mod running; pub(super) use factory::Factory; -pub(super) use running::Running; +pub(super) use running::Auction; #[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] pub(super) struct Id([u8; 32]); @@ -101,7 +101,7 @@ enum Command { StartTimer, } -struct Auction { +struct Worker { /// The sequencer's ABCI client, used for submitting transactions sequencer_abci_client: sequencer_client::HttpClient, /// Channel for receiving commands sent via the handle @@ -123,7 +123,7 @@ struct Auction { pending_nonce: PendingNonceSubscriber, } -impl Auction { +impl Worker { #[instrument(skip_all, fields(id = %self.id))] pub(super) async fn run(mut self) -> eyre::Result<()> { let mut latency_margin_timer = None; diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs index 69afc54447..802e186994 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs @@ -23,19 +23,18 @@ use crate::{ flatten_join_result, }; -pub(in crate::auctioneer::inner) struct Running { +pub(in crate::auctioneer::inner) struct Auction { pub(super) id: super::Id, pub(super) height: u64, pub(super) parent_block_of_executed: Option<[u8; 32]>, - // TODO: Rename this to AuctionSender or smth like that pub(super) commands: mpsc::Sender, pub(super) bundles: mpsc::Sender, - pub(super) task: JoinHandle>, + pub(super) worker: JoinHandle>, } -impl Running { +impl Auction { pub(in crate::auctioneer::inner) fn abort(&self) { - self.task.abort(); + self.worker.abort(); } pub(in crate::auctioneer::inner) fn id(&self) -> &super::Id { @@ -151,14 +150,14 @@ impl Running { } } -impl Future for Running { +impl Future for Auction { type Output = (super::Id, eyre::Result<()>); fn poll( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - let res = std::task::ready!(self.task.poll_unpin(cx)); + let res = std::task::ready!(self.worker.poll_unpin(cx)); std::task::Poll::Ready((self.id, flatten_join_result(res))) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 5e1e39300d..3a4c164d99 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -56,9 +56,9 @@ pub(super) struct Inner { auction_factory: auction::Factory, block_commitments: BlockCommitmentStream, bundles: BundleStream, - cancelled_auctions: FuturesUnordered, + cancelled_auctions: FuturesUnordered, executed_blocks: ExecuteOptimisticBlockStream, - running_auction: Option, + running_auction: Option, optimistic_blocks: OptimisticBlockStream, pending_nonce: PendingNoncePublisher, rollup_id: RollupId, From 07aebdf9579e53f907eb5609dc3097ab96243e31 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Sat, 30 Nov 2024 16:49:18 +0100 Subject: [PATCH 52/73] swap the module relationship between auction future and worker task --- .../src/auctioneer/inner/auction/mod.rs | 261 +++++++++--------- .../src/auctioneer/inner/auction/running.rs | 163 ----------- .../src/auctioneer/inner/auction/worker.rs | 192 +++++++++++++ 3 files changed, 328 insertions(+), 288 deletions(-) delete mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs create mode 100644 crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 5993e596d5..dd57cd1afe 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -37,45 +37,41 @@ //! as it received the signal to start the timer. This corresponds to the sequencer block being //! committed, thus providing the latest pending nonce. -use std::time::Duration; - -use allocation_rule::FirstPrice; use astria_core::{ - primitive::v1::{ - asset, - RollupId, - }, + self, sequencerblock::v1::block::BlockHash, }; use astria_eyre::eyre::{ self, - eyre, Context, - OptionExt as _, }; -use sequencer_client::SequencerClientExt; +use futures::{ + Future, + FutureExt as _, +}; use tokio::{ - select, sync::mpsc, + task::JoinHandle, }; use tracing::{ - debug, - error, info, instrument, + warn, }; use super::PendingNonceSubscriber; use crate::{ + block::Commitment, bundle::Bundle, + flatten_join_result, sequencer_key::SequencerKey, }; -mod allocation_rule; pub(super) mod factory; -mod running; pub(super) use factory::Factory; -pub(super) use running::Auction; +mod allocation_rule; +mod worker; +use worker::Worker; #[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] pub(super) struct Id([u8; 32]); @@ -101,122 +97,137 @@ enum Command { StartTimer, } -struct Worker { - /// The sequencer's ABCI client, used for submitting transactions - sequencer_abci_client: sequencer_client::HttpClient, - /// Channel for receiving commands sent via the handle - commands_rx: mpsc::Receiver, - /// Channel for receiving new bundles - bundles_rx: mpsc::Receiver, - /// The time between receiving a block commitment - latency_margin: Duration, - /// The ID of the auction +pub(super) struct Auction { id: Id, - /// The key used to sign transactions on the sequencer - sequencer_key: SequencerKey, - /// Fee asset for submitting transactions - fee_asset_denomination: asset::Denom, - /// The chain ID used for sequencer transactions - sequencer_chain_id: String, - /// Rollup ID to submit the auction result to - rollup_id: RollupId, - pending_nonce: PendingNonceSubscriber, + height: u64, + parent_block_of_executed: Option<[u8; 32]>, + commands: mpsc::Sender, + bundles: mpsc::Sender, + worker: JoinHandle>, } -impl Worker { - #[instrument(skip_all, fields(id = %self.id))] - pub(super) async fn run(mut self) -> eyre::Result<()> { - let mut latency_margin_timer = None; - // TODO: do we want to make this configurable to allow for more complex allocation rules? - let mut allocation_rule = FirstPrice::new(); - let mut auction_is_open = false; - - let auction_result = loop { - select! { - biased; - - // get the auction winner when the timer expires - _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { - break Ok(allocation_rule.winner()); - } - - Some(cmd) = self.commands_rx.recv() => { - match cmd { - Command::StartProcessingBids => { - if auction_is_open { - break Err(eyre!("auction received signal to start processing bids twice")); - } - auction_is_open = true; - }, - Command::StartTimer => { - if !auction_is_open { - break Err(eyre!("auction received signal to start timer before signal to start processing bids")); - } - - // set the timer - latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); - } - } - } - - Some(bundle) = self.bundles_rx.recv(), if auction_is_open => { - if allocation_rule.bid(bundle.clone()) { - info!( - auction.id = %self.id, - bundle.bid = %bundle.bid(), - "received new highest bid" - ); - } else { - debug!( - auction.id = %self.id, - bundle.bid = %bundle.bid(), - "received bid lower than current highest bid, discarding" - ); - } - } - - } - }; +impl Auction { + pub(super) fn abort(&self) { + self.worker.abort(); + } + + pub(in crate::auctioneer::inner) fn id(&self) -> &Id { + &self.id + } + + #[instrument(skip(self))] + // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(super) fn start_timer(&mut self, block_commitment: Commitment) -> eyre::Result<()> { + let id_according_to_block = + Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); + + if self.id == id_according_to_block && block_commitment.sequencer_height() == self.height { + self.commands + .try_send(Command::StartTimer) + .wrap_err("failed to send command to start timer to auction")?; + } else { + // TODO: provide better information on the blocks/currently running auction. + // warn!( + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // block_commitment.sequencer_block_hash = + // %base64(block_commitment.sequencer_block_hash()), "received + // block commitment for the wrong block" ); + info!( + "not starting the auction timer because sequencer block hash and height of the \ + commitment did not match that of the running auction", + ); + } + + Ok(()) + } - // TODO: report the pending nonce that we ended up using. - let transaction_body = auction_result - .wrap_err("auction failed unexpectedly")? - .ok_or_eyre("auction ended with no winning bid")? - .into_transaction_body( - self.pending_nonce.get(), - self.rollup_id, - &self.sequencer_key, - self.fee_asset_denomination.clone(), - self.sequencer_chain_id, + #[instrument(skip(self))] + // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> + // eyre::Result<()> { + pub(in crate::auctioneer::inner) fn start_processing_bids( + &mut self, + block: crate::block::Executed, + ) -> eyre::Result<()> { + let id_according_to_block = Id::from_sequencer_block_hash(block.sequencer_block_hash()); + + if self.id == id_according_to_block { + // TODO: What if it was already set? Overwrite? Replace? Drop? + let _ = self + .parent_block_of_executed + .replace(block.parent_rollup_block_hash()); + self.commands + .try_send(Command::StartProcessingBids) + .wrap_err("failed to send command to start processing bids")?; + } else { + // TODO: bring back the fields to track the dropped block and current block + // warn!( + // // TODO: nicer display for the current block + // current_block.sequencer_block_hash = + // %base64(self.current_block.sequencer_block_hash()), + // executed_block.sequencer_block_hash = + // %base64(executed_block.sequencer_block_hash()), + // executed_block.rollup_block_hash = + // %base64(executed_block.rollup_block_hash()), "received + // optimistic execution result for wrong sequencer block" ); + warn!( + "not starting to process bids in the current auction because we received an \ + executed block from the rollup with a sequencer block hash that does not match \ + that of the currently running auction; dropping the executed block" ); + } - let transaction = transaction_body.sign(self.sequencer_key.signing_key()); - - // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, - // it's likey lost. - // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once - // if the response didn't take too long? But it's probably a bad idea to even try. - // Can we detect if a submission failed due to a bad nonce? In this case, we could - // immediately ("optimistically") submit with the most recent pending nonce (if the - // publisher updated it in the meantime) or just nonce + 1 (if it didn't yet update)? - match self - .sequencer_abci_client - .submit_transaction_sync(transaction) - .await - .wrap_err("submission of the auction failed; it's likely lost") - { - Ok(resp) => { - // TODO: provide tx_sync response hash? Does it have extra meaning? - info!( - response.log = %resp.log, - response.code = resp.code.value(), - "auction winner submitted to sequencer", - ); - } - Err(error) => { - error!(%error, "failed to submit auction winner to sequencer; it's likely lost"); - } + Ok(()) + } + + pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( + &mut self, + bundle: Bundle, + ) -> eyre::Result<()> { + let id_according_to_bundle = + Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); + + // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, + // tacked block hash, provided block hash, etc. + let Some(parent_block_of_executed) = self.parent_block_of_executed else { + eyre::bail!( + "received a new bundle but the current auction has not yet + received an execute block from the rollup; dropping the bundle" + ); + }; + let ids_match = self.id == id_according_to_bundle; + let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); + if ids_match && parent_blocks_match { + self.bundles + .try_send(bundle) + .wrap_err("failed to add bundle to auction")?; + } else { + warn!( + // TODO: Add these fields back in. Is it even necessary to return the error? + // Can't we just fire the event here? necessary? + // + // curent_block.sequencer_block_hash = %base64(self. + // current_block.sequencer_block_hash()), + // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), + // bundle.parent_rollup_block_hash = + // %base64(bundle.parent_rollup_block_hash()), + "incoming bundle does not match current block, ignoring" + ); + eyre::bail!("auction ID and ID according to bundle don't match"); } Ok(()) } } + +impl Future for Auction { + type Output = (Id, eyre::Result<()>); + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let res = std::task::ready!(self.worker.poll_unpin(cx)); + std::task::Poll::Ready((self.id, flatten_join_result(res))) + } +} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs deleted file mode 100644 index 802e186994..0000000000 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/running.rs +++ /dev/null @@ -1,163 +0,0 @@ -use astria_eyre::eyre::{ - self, - WrapErr as _, -}; -use futures::{ - Future, - FutureExt, -}; -use tokio::{ - sync::mpsc, - task::JoinHandle, -}; -use tracing::{ - info, - instrument, - warn, -}; - -use super::Command; -use crate::{ - block::Commitment, - bundle::Bundle, - flatten_join_result, -}; - -pub(in crate::auctioneer::inner) struct Auction { - pub(super) id: super::Id, - pub(super) height: u64, - pub(super) parent_block_of_executed: Option<[u8; 32]>, - pub(super) commands: mpsc::Sender, - pub(super) bundles: mpsc::Sender, - pub(super) worker: JoinHandle>, -} - -impl Auction { - pub(in crate::auctioneer::inner) fn abort(&self) { - self.worker.abort(); - } - - pub(in crate::auctioneer::inner) fn id(&self) -> &super::Id { - &self.id - } - - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> - // eyre::Result<()> { - pub(in crate::auctioneer::inner) fn start_timer( - &mut self, - block_commitment: Commitment, - ) -> eyre::Result<()> { - let id_according_to_block = - super::Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); - - if self.id == id_according_to_block && block_commitment.sequencer_height() == self.height { - self.commands - .try_send(Command::StartTimer) - .wrap_err("failed to send command to start timer to auction")?; - } else { - // TODO: provide better information on the blocks/currently running auction. - // warn!( - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // block_commitment.sequencer_block_hash = - // %base64(block_commitment.sequencer_block_hash()), "received - // block commitment for the wrong block" ); - info!( - "not starting the auction timer because sequencer block hash and height of the \ - commitment did not match that of the running auction", - ); - } - - Ok(()) - } - - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> - // eyre::Result<()> { - pub(in crate::auctioneer::inner) fn start_processing_bids( - &mut self, - block: crate::block::Executed, - ) -> eyre::Result<()> { - let id_according_to_block = - super::Id::from_sequencer_block_hash(block.sequencer_block_hash()); - - if self.id == id_according_to_block { - // TODO: What if it was already set? Overwrite? Replace? Drop? - let _ = self - .parent_block_of_executed - .replace(block.parent_rollup_block_hash()); - self.commands - .try_send(Command::StartProcessingBids) - .wrap_err("failed to send command to start processing bids")?; - } else { - // TODO: bring back the fields to track the dropped block and current block - // warn!( - // // TODO: nicer display for the current block - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // executed_block.sequencer_block_hash = - // %base64(executed_block.sequencer_block_hash()), - // executed_block.rollup_block_hash = - // %base64(executed_block.rollup_block_hash()), "received - // optimistic execution result for wrong sequencer block" ); - warn!( - "not starting to process bids in the current auction because we received an \ - executed block from the rollup with a sequencer block hash that does not match \ - that of the currently running auction; dropping the executed block" - ); - } - - Ok(()) - } - - pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( - &mut self, - bundle: Bundle, - ) -> eyre::Result<()> { - let id_according_to_bundle = - super::Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); - - // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, - // tacked block hash, provided block hash, etc. - let Some(parent_block_of_executed) = self.parent_block_of_executed else { - eyre::bail!( - "received a new bundle but the current auction has not yet - received an execute block from the rollup; dropping the bundle" - ); - }; - let ids_match = self.id == id_according_to_bundle; - let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); - if ids_match && parent_blocks_match { - self.bundles - .try_send(bundle) - .wrap_err("failed to add bundle to auction")?; - } else { - warn!( - // TODO: Add these fields back in. Is it even necessary to return the error? - // Can't we just fire the event here? necessary? - // - // curent_block.sequencer_block_hash = %base64(self. - // current_block.sequencer_block_hash()), - // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - // bundle.parent_rollup_block_hash = - // %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - eyre::bail!("auction ID and ID according to bundle don't match"); - } - Ok(()) - } -} - -impl Future for Auction { - type Output = (super::Id, eyre::Result<()>); - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let res = std::task::ready!(self.worker.poll_unpin(cx)); - std::task::Poll::Ready((self.id, flatten_join_result(res))) - } -} diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs new file mode 100644 index 0000000000..e4d4ee9f04 --- /dev/null +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -0,0 +1,192 @@ +//! The Auction is repsonsible for running an auction for a given block. An auction advances through +//! the following states, controlled via the `commands_rx` channel received: +//! 1. The auction is initialized but not yet started (i.e. no commands have been received). +//! 2. After receiving a `Command::StartProcessingBids`, the auction will start processing incoming +//! bundles from `new_bundles_rx`. +//! 3. After receiving a `Command::StartTimer`, the auction will set a timer for `latency_margin` +//! (denominated in milliseconds). +//! 4. Once the timer expires, the auction will choose a winner based on its `AllocationRule` and +//! submit it to the sequencer. +//! +//! ## Aborting an Auction +//! The auction may also be aborted at any point before the timer expires. +//! This will cause the auction to return early without submitting a winner, +//! effectively discarding any bundles that were processed. +//! This is used for leveraging optimsitic execution, running an auction for block data that has +//! been proposed in the sequencer network's cometBFT but not yet finalized. +//! We assume that most proposals are adopted in cometBFT, allowing us to buy a few hundred +//! milliseconds before they are finalized. However, if multiple rounds of voting invalidate a +//! proposal, we can abort the auction and avoid submitting a potentially invalid bundle. In this +//! case, the auction will abort and a new one will be created for the newly processed proposal +//! (which will be received by the Optimistic Executor via the optimistic block stream). +//! +//! ## Auction Result +//! The auction result is a `Bundle` that is signed by the Auctioneer and submitted to the rollup +//! via the sequencer. The rollup defines a trusted Auctioneer address that it allows to submit +//! bundles, and thus must verify the Auctioneer's signature over this bundle. +//! +//! Since the sequencer does not include the transaction signer's metadata with the `RollupData` +//! events that it saves in its block data, the Auctioneer must include this metadata in its +//! `RollupDataSubmission`s. This is done by wrapping the winning `Bundle` object in an +//! `AuctionResult` object, which is then serialized into the `RollupDataSubmission`. +//! +//! ## Submission to Sequencer +//! The auction will submit the winning bundle to the sequencer via the `broadcast_tx_sync` ABCI(?) +//! endpoint. +//! In order to save time on fetching a nonce, the auction will fetch the next pending nonce as soon +//! as it received the signal to start the timer. This corresponds to the sequencer block being +//! committed, thus providing the latest pending nonce. + +use std::time::Duration; + +use astria_core::primitive::v1::{ + asset, + RollupId, +}; +use astria_eyre::eyre::{ + self, + eyre, + Context, + OptionExt as _, +}; +use sequencer_client::SequencerClientExt; +use tokio::{ + select, + sync::mpsc, +}; +use tracing::{ + debug, + error, + info, + instrument, +}; + +use super::{ + allocation_rule::FirstPrice, + Command, + PendingNonceSubscriber, +}; +use crate::{ + bundle::Bundle, + sequencer_key::SequencerKey, +}; + +pub(super) struct Worker { + /// The sequencer's ABCI client, used for submitting transactions + pub(super) sequencer_abci_client: sequencer_client::HttpClient, + /// Channel for receiving commands sent via the handle + pub(super) commands_rx: mpsc::Receiver, + /// Channel for receiving new bundles + pub(super) bundles_rx: mpsc::Receiver, + /// The time between receiving a block commitment + pub(super) latency_margin: Duration, + /// The ID of the auction + pub(super) id: super::Id, + /// The key used to sign transactions on the sequencer + pub(super) sequencer_key: SequencerKey, + /// Fee asset for submitting transactions + pub(super) fee_asset_denomination: asset::Denom, + /// The chain ID used for sequencer transactions + pub(super) sequencer_chain_id: String, + /// Rollup ID to submit the auction result to + pub(super) rollup_id: RollupId, + pub(super) pending_nonce: PendingNonceSubscriber, +} + +impl Worker { + #[instrument(skip_all, fields(id = %self.id))] + pub(super) async fn run(mut self) -> eyre::Result<()> { + let mut latency_margin_timer = None; + // TODO: do we want to make this configurable to allow for more complex allocation rules? + let mut allocation_rule = FirstPrice::new(); + let mut auction_is_open = false; + + let auction_result = loop { + select! { + biased; + + // get the auction winner when the timer expires + _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { + break Ok(allocation_rule.winner()); + } + + Some(cmd) = self.commands_rx.recv() => { + match cmd { + Command::StartProcessingBids => { + if auction_is_open { + break Err(eyre!("auction received signal to start processing bids twice")); + } + auction_is_open = true; + }, + Command::StartTimer => { + if !auction_is_open { + break Err(eyre!("auction received signal to start timer before signal to start processing bids")); + } + + // set the timer + latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); + } + } + } + + Some(bundle) = self.bundles_rx.recv(), if auction_is_open => { + if allocation_rule.bid(bundle.clone()) { + info!( + auction.id = %self.id, + bundle.bid = %bundle.bid(), + "received new highest bid" + ); + } else { + debug!( + auction.id = %self.id, + bundle.bid = %bundle.bid(), + "received bid lower than current highest bid, discarding" + ); + } + } + + } + }; + + // TODO: report the pending nonce that we ended up using. + let transaction_body = auction_result + .wrap_err("auction failed unexpectedly")? + .ok_or_eyre("auction ended with no winning bid")? + .into_transaction_body( + self.pending_nonce.get(), + self.rollup_id, + &self.sequencer_key, + self.fee_asset_denomination.clone(), + self.sequencer_chain_id, + ); + + let transaction = transaction_body.sign(self.sequencer_key.signing_key()); + + // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, + // it's likey lost. + // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once + // if the response didn't take too long? But it's probably a bad idea to even try. + // Can we detect if a submission failed due to a bad nonce? In this case, we could + // immediately ("optimistically") submit with the most recent pending nonce (if the + // publisher updated it in the meantime) or just nonce + 1 (if it didn't yet update)? + match self + .sequencer_abci_client + .submit_transaction_sync(transaction) + .await + .wrap_err("submission of the auction failed; it's likely lost") + { + Ok(resp) => { + // TODO: provide tx_sync response hash? Does it have extra meaning? + info!( + response.log = %resp.log, + response.code = resp.code.value(), + "auction winner submitted to sequencer", + ); + } + Err(error) => { + error!(%error, "failed to submit auction winner to sequencer; it's likely lost"); + } + } + Ok(()) + } +} From 0deb18e7dbb6d2176120e9d3d3c42e0e2f4cc5a7 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 2 Dec 2024 14:30:49 +0100 Subject: [PATCH 53/73] instrument all auction handlers, separate auction ID from block hashes --- .../src/auctioneer/inner/auction/factory.rs | 8 +- .../src/auctioneer/inner/auction/mod.rs | 140 +++++++----------- .../src/auctioneer/inner/mod.rs | 9 +- crates/astria-auctioneer/src/block/mod.rs | 16 +- 4 files changed, 69 insertions(+), 104 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 58c8b56df9..f1f50bc601 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -33,7 +33,8 @@ impl Factory { &self, block: &FilteredSequencerBlock, ) -> Auction { - let auction_id = super::Id::from_sequencer_block_hash(block.block_hash()); + let id = super::Id::from_sequencer_block_hash(block.block_hash()); + let block_hash = *block.block_hash(); let height = block.height().into(); // TODO: get the capacities from config or something instead of using a magic number @@ -45,7 +46,7 @@ impl Factory { commands_rx, bundles_rx, latency_margin: self.latency_margin, - id: auction_id, + id, sequencer_key: self.sequencer_key.clone(), fee_asset_denomination: self.fee_asset_denomination.clone(), sequencer_chain_id: self.sequencer_chain_id.clone(), @@ -54,7 +55,8 @@ impl Factory { }; Auction { - id: auction_id, + id, + block_hash, height, parent_block_of_executed: None, commands: commands_tx, diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index dd57cd1afe..29f2ef35c4 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -43,21 +43,19 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, + ensure, Context, }; use futures::{ Future, FutureExt as _, }; +use telemetry::display::base64; use tokio::{ sync::mpsc, task::JoinHandle, }; -use tracing::{ - info, - instrument, - warn, -}; +use tracing::instrument; use super::PendingNonceSubscriber; use crate::{ @@ -99,6 +97,7 @@ enum Command { pub(super) struct Auction { id: Id, + block_hash: BlockHash, height: u64, parent_block_of_executed: Option<[u8; 32]>, commands: mpsc::Sender, @@ -115,79 +114,54 @@ impl Auction { &self.id } - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_timer(&mut self, auction_id: Id) -> - // eyre::Result<()> { - pub(super) fn start_timer(&mut self, block_commitment: Commitment) -> eyre::Result<()> { - let id_according_to_block = - Id::from_sequencer_block_hash(block_commitment.sequencer_block_hash()); - - if self.id == id_according_to_block && block_commitment.sequencer_height() == self.height { - self.commands - .try_send(Command::StartTimer) - .wrap_err("failed to send command to start timer to auction")?; - } else { - // TODO: provide better information on the blocks/currently running auction. - // warn!( - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // block_commitment.sequencer_block_hash = - // %base64(block_commitment.sequencer_block_hash()), "received - // block commitment for the wrong block" ); - info!( - "not starting the auction timer because sequencer block hash and height of the \ - commitment did not match that of the running auction", - ); - } - - Ok(()) + #[instrument(skip_all, fields(id = %self.id), err)] + pub(super) fn start_timer(&mut self, commitment: Commitment) -> eyre::Result<()> { + ensure!( + &self.block_hash == commitment.block_hash() && self.height == commitment.height(), + "commitment does not match auction; auction.block_hash = `{}`, auction.height = `{}`, \ + commitment.block_hash = `{}`, commitment.height = `{}`", + self.block_hash, + self.height, + commitment.block_hash(), + commitment.height(), + ); + self.commands + .try_send(Command::StartTimer) + .wrap_err("failed to send command to start timer to auction") } - #[instrument(skip(self))] - // pub(in crate::auctioneer::inner) fn start_processing_bids(&mut self, auction_id: Id) -> - // eyre::Result<()> { + #[instrument(skip_all, fields(id = %self.id), err)] pub(in crate::auctioneer::inner) fn start_processing_bids( &mut self, block: crate::block::Executed, ) -> eyre::Result<()> { - let id_according_to_block = Id::from_sequencer_block_hash(block.sequencer_block_hash()); - - if self.id == id_according_to_block { - // TODO: What if it was already set? Overwrite? Replace? Drop? - let _ = self - .parent_block_of_executed - .replace(block.parent_rollup_block_hash()); - self.commands - .try_send(Command::StartProcessingBids) - .wrap_err("failed to send command to start processing bids")?; - } else { - // TODO: bring back the fields to track the dropped block and current block - // warn!( - // // TODO: nicer display for the current block - // current_block.sequencer_block_hash = - // %base64(self.current_block.sequencer_block_hash()), - // executed_block.sequencer_block_hash = - // %base64(executed_block.sequencer_block_hash()), - // executed_block.rollup_block_hash = - // %base64(executed_block.rollup_block_hash()), "received - // optimistic execution result for wrong sequencer block" ); - warn!( - "not starting to process bids in the current auction because we received an \ - executed block from the rollup with a sequencer block hash that does not match \ - that of the currently running auction; dropping the executed block" - ); - } - - Ok(()) + ensure!( + &self.block_hash == block.sequencer_block_hash(), + "executed block does not match auction; auction.block_hash = `{}`, \ + executed.block_hash = `{}`", + &self.block_hash, + block.sequencer_block_hash(), + ); + // TODO: What if it was already set? Overwrite? Replace? Drop? + let _ = self + .parent_block_of_executed + .replace(block.parent_rollup_block_hash()); + self.commands + .try_send(Command::StartProcessingBids) + .wrap_err("failed to send command to start processing bids") } + // TODO: Use a refinement type for the parente rollup block hash + #[instrument(skip_all, fields( + id = %self.id, + bundle.sequencer_block_hash = %bundle.base_sequencer_block_hash(), + bundle.parent_roll_block_hash = %base64(bundle.parent_rollup_block_hash()), + + ), err)] pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( &mut self, bundle: Bundle, ) -> eyre::Result<()> { - let id_according_to_bundle = - Id::from_sequencer_block_hash(bundle.base_sequencer_block_hash()); - // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, // tacked block hash, provided block hash, etc. let Some(parent_block_of_executed) = self.parent_block_of_executed else { @@ -196,27 +170,19 @@ impl Auction { received an execute block from the rollup; dropping the bundle" ); }; - let ids_match = self.id == id_according_to_bundle; - let parent_blocks_match = parent_block_of_executed == bundle.parent_rollup_block_hash(); - if ids_match && parent_blocks_match { - self.bundles - .try_send(bundle) - .wrap_err("failed to add bundle to auction")?; - } else { - warn!( - // TODO: Add these fields back in. Is it even necessary to return the error? - // Can't we just fire the event here? necessary? - // - // curent_block.sequencer_block_hash = %base64(self. - // current_block.sequencer_block_hash()), - // bundle.sequencer_block_hash = %base64(bundle.base_sequencer_block_hash()), - // bundle.parent_rollup_block_hash = - // %base64(bundle.parent_rollup_block_hash()), - "incoming bundle does not match current block, ignoring" - ); - eyre::bail!("auction ID and ID according to bundle don't match"); - } - Ok(()) + ensure!( + &self.block_hash == bundle.base_sequencer_block_hash() + && parent_block_of_executed == bundle.parent_rollup_block_hash(), + "bundle does not match auction; auction.sequenecer_block_hash = `{}`, \ + auction.parent_block_hash = `{}`, bundle. = `{}`, bundle.height = `{}`", + self.block_hash, + base64(parent_block_of_executed), + bundle.base_sequencer_block_hash(), + base64(bundle.parent_rollup_block_hash()), + ); + self.bundles + .try_send(bundle) + .wrap_err("failed to submit bundle to auction") } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 3a4c164d99..23448015a3 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -251,13 +251,10 @@ impl Inner { #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_block_commitment( &mut self, - block_commitment: eyre::Result, + commitment: eyre::Result, ) -> eyre::Result<()> { - let block_commitment = block_commitment.wrap_err("failed to receive block commitment")?; - Span::current().record( - "block_hash", - field::display(block_commitment.sequencer_block_hash()), - ); + let block_commitment = commitment.wrap_err("failed to receive block commitment")?; + Span::current().record("block_hash", field::display(block_commitment.block_hash())); if let Some(running_auction) = &mut self.running_auction { running_auction diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index ff2c4f092e..cc1758e3ae 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -143,9 +143,9 @@ impl Executed { // This is very confusing. pub(crate) struct Commitment { /// The height of the sequencer block that was committed. - sequencer_height: u64, + height: u64, /// The hash of the sequencer block that was committed. - sequnecer_block_hash: BlockHash, + block_hash: BlockHash, } impl Commitment { @@ -153,8 +153,8 @@ impl Commitment { raw: &raw_optimistic_block::SequencerBlockCommit, ) -> eyre::Result { Ok(Self { - sequencer_height: raw.height, - sequnecer_block_hash: raw + height: raw.height, + block_hash: raw .block_hash .as_ref() .try_into() @@ -162,12 +162,12 @@ impl Commitment { }) } - pub(crate) fn sequencer_block_hash(&self) -> &BlockHash { - &self.sequnecer_block_hash + pub(crate) fn block_hash(&self) -> &BlockHash { + &self.block_hash } /// The height of the sequencer block that was committed. - pub(crate) fn sequencer_height(&self) -> u64 { - self.sequencer_height + pub(crate) fn height(&self) -> u64 { + self.height } } From e16952bb13450c2b9516dd042e2e22d77d447db4 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 2 Dec 2024 15:55:17 +0100 Subject: [PATCH 54/73] remove connect timeouts --- crates/astria-auctioneer/src/rollup_channel.rs | 1 - crates/astria-auctioneer/src/sequencer_channel.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 1e00ed8c09..60aa545652 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -48,7 +48,6 @@ impl RollupChannel { fn create(uri: &str) -> eyre::Result { let channel = Channel::from_shared(uri.to_string()) .wrap_err("failed to open a channel to the provided uri")? - .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(2)) .connect_lazy(); diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index e5e28ece99..d12055fcaf 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -55,7 +55,6 @@ impl SequencerChannel { fn create(uri: &str) -> eyre::Result { let channel = Channel::from_shared(uri.to_string()) .wrap_err("failed to open a channel to the provided uri")? - .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(2)) .connect_lazy(); From 37568f806fb26d465bb1d7ced97aff28ef107c6a Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 2 Dec 2024 18:04:45 +0100 Subject: [PATCH 55/73] instrument grpc at the request/response level --- Cargo.lock | 44 ++++++++++++----- crates/astria-auctioneer/Cargo.toml | 5 ++ .../astria-auctioneer/src/rollup_channel.rs | 17 +++---- .../src/sequencer_channel.rs | 16 +++---- .../astria-auctioneer/src/streaming_utils.rs | 48 +++++++++++++++++++ 5 files changed, 98 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8658964d7e..c09d7339fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,7 +511,10 @@ dependencies = [ "bytes", "futures", "hex", + "http 0.2.12", + "http-body 0.4.6", "humantime", + "hyper 0.14.30", "itertools 0.12.1", "pbjson-types", "pin-project-lite", @@ -522,6 +525,8 @@ dependencies = [ "tokio-stream", "tokio-util 0.7.12", "tonic 0.10.2", + "tower 0.5.1", + "tower-http", "tracing", "tryhard", ] @@ -709,7 +714,7 @@ dependencies = [ "tokio-stream", "tokio-util 0.7.12", "tonic 0.10.2", - "tower", + "tower 0.4.13", "tracing", "tryhard", "wiremock", @@ -860,7 +865,7 @@ dependencies = [ "thiserror", "tokio", "tonic 0.10.2", - "tower", + "tower 0.4.13", "tower-abci", "tower-actor", "tower-http", @@ -944,7 +949,7 @@ dependencies = [ "tokio-test", "tokio-util 0.7.12", "tonic 0.10.2", - "tower", + "tower 0.4.13", "tracing", "tryhard", "wiremock", @@ -1169,7 +1174,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -4771,7 +4776,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", ] @@ -4808,7 +4813,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util 0.7.12", - "tower", + "tower 0.4.13", "tracing", ] @@ -5829,7 +5834,7 @@ dependencies = [ "tendermint-light-client-verifier", "time", "tonic 0.10.2", - "tower", + "tower 0.4.13", "tracing", ] @@ -6024,7 +6029,7 @@ dependencies = [ "tokio-stream", "tokio-util 0.7.12", "tonic 0.10.2", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -8164,7 +8169,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -8192,7 +8197,7 @@ dependencies = [ "prost", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -8245,6 +8250,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-abci" version = "0.12.0" @@ -8260,7 +8279,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util 0.6.10", - "tower", + "tower 0.4.13", "tracing", ] @@ -8275,7 +8294,7 @@ dependencies = [ "thiserror", "tokio", "tokio-util 0.7.12", - "tower", + "tower 0.4.13", "tracing", ] @@ -8295,6 +8314,7 @@ dependencies = [ "pin-project-lite", "tower-layer", "tower-service", + "tracing", ] [[package]] diff --git a/crates/astria-auctioneer/Cargo.toml b/crates/astria-auctioneer/Cargo.toml index 4d6998664b..4a70b7f5eb 100644 --- a/crates/astria-auctioneer/Cargo.toml +++ b/crates/astria-auctioneer/Cargo.toml @@ -42,6 +42,11 @@ tonic = { workspace = true } tokio-stream = { workspace = true, features = ["sync"] } pin-project-lite = "0.2.15" +tower-http = { version = "0.4.4", features = ["map-response-body", "trace"] } +tower = { version = "0.5.1", features = ["util"] } +hyper.workspace = true +http = "0.2.11" +http-body = "0.4.5" [dev-dependencies] config = { package = "astria-config", path = "../astria-config", features = [ diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 60aa545652..9cbb71fe42 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -5,7 +5,6 @@ use std::{ Context, Poll, }, - time::Duration, }; use astria_core::generated::bundle::v1alpha1::{ @@ -27,11 +26,14 @@ use futures::{ use prost::Name as _; use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; -use tonic::transport::Channel; use crate::{ bundle::Bundle, - streaming_utils::restarting_stream, + streaming_utils::{ + make_instrumented_channel, + restarting_stream, + InstrumentedChannel, + }, }; pub(crate) fn open(endpoint: &str) -> eyre::Result { @@ -41,18 +43,13 @@ pub(crate) fn open(endpoint: &str) -> eyre::Result { #[derive(Clone)] pub(crate) struct RollupChannel { - inner: Channel, + inner: InstrumentedChannel, } impl RollupChannel { fn create(uri: &str) -> eyre::Result { - let channel = Channel::from_shared(uri.to_string()) - .wrap_err("failed to open a channel to the provided uri")? - .timeout(Duration::from_secs(2)) - .connect_lazy(); - Ok(Self { - inner: channel, + inner: make_instrumented_channel(uri)?, }) } diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index d12055fcaf..892995dd03 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -5,7 +5,6 @@ use std::{ Context, Poll, }, - time::Duration, }; use astria_core::{ @@ -34,11 +33,13 @@ use futures::{ StreamExt as _, }; use prost::Name; -use tonic::transport::Channel; use crate::{ block::Commitment, - streaming_utils::restarting_stream, + streaming_utils::{ + restarting_stream, + InstrumentedChannel, + }, }; pub(crate) fn open(endpoint: &str) -> eyre::Result { @@ -48,18 +49,13 @@ pub(crate) fn open(endpoint: &str) -> eyre::Result { #[derive(Clone)] pub(crate) struct SequencerChannel { - inner: Channel, + inner: InstrumentedChannel, } impl SequencerChannel { fn create(uri: &str) -> eyre::Result { - let channel = Channel::from_shared(uri.to_string()) - .wrap_err("failed to open a channel to the provided uri")? - .timeout(Duration::from_secs(2)) - .connect_lazy(); - Ok(Self { - inner: channel, + inner: crate::streaming_utils::make_instrumented_channel(uri)?, }) } diff --git a/crates/astria-auctioneer/src/streaming_utils.rs b/crates/astria-auctioneer/src/streaming_utils.rs index cad02ca32e..abb8bbc572 100644 --- a/crates/astria-auctioneer/src/streaming_utils.rs +++ b/crates/astria-auctioneer/src/streaming_utils.rs @@ -4,15 +4,63 @@ use std::{ ready, Poll, }, + time::Duration, }; +use astria_eyre::eyre::{ + self, + WrapErr as _, +}; +use bytes::Bytes; use futures::{ Future, FutureExt as _, Stream, StreamExt as _, }; +use http::{ + Request, + Response, +}; +use http_body::combinators::UnsyncBoxBody; use pin_project_lite::pin_project; +use tonic::{ + transport::Channel, + Status, +}; +use tower::{ + util::BoxCloneService, + ServiceBuilder, +}; +use tower_http::{ + map_response_body::MapResponseBodyLayer, + trace::{ + DefaultMakeSpan, + TraceLayer, + }, +}; + +pub(crate) type InstrumentedChannel = BoxCloneService< + Request>, + Response>, + tonic::transport::Error, +>; + +pub(crate) fn make_instrumented_channel(uri: &str) -> eyre::Result { + let channel = Channel::from_shared(uri.to_string()) + .wrap_err("failed to create a channel to the provided uri")? + .timeout(Duration::from_secs(2)) + .connect_lazy(); + + let channel = ServiceBuilder::new() + .layer(MapResponseBodyLayer::new(UnsyncBoxBody::new)) + .layer( + TraceLayer::new_for_grpc().make_span_with(DefaultMakeSpan::new().include_headers(true)), + ) + .service(channel); + + Ok(InstrumentedChannel::new(channel)) +} pub(crate) fn restarting_stream(f: F) -> RestartingStream where From 60e9da14decce1d396039d2e4667e74b8f6d55d1 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Mon, 2 Dec 2024 18:32:50 +0100 Subject: [PATCH 56/73] emit events for failed stream connection requests; also bring back connection timeouts so the spans actually end --- crates/astria-auctioneer/src/rollup_channel.rs | 13 +++++++++---- .../astria-auctioneer/src/sequencer_channel.rs | 17 +++++++++++++---- crates/astria-auctioneer/src/streaming_utils.rs | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 9cbb71fe42..74626ec918 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -26,6 +26,11 @@ use futures::{ use prost::Name as _; use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; +use tracing::{ + info_span, + warn, + Instrument as _, +}; use crate::{ bundle::Bundle, @@ -63,13 +68,13 @@ impl RollupChannel { .get_bundle_stream(GetBundleStreamRequest {}) .await .map(tonic::Response::into_inner) - // TODO: Don't quietly swallow this error. Provide some form of - // logging. + .inspect_err(|error| warn!(%error, "request to open bundle stream failed")) .ok()?; Some(InnerBundleStream { inner, }) } + .instrument(info_span!("request bundle stream")) }) .boxed(); BundleStream { @@ -106,13 +111,13 @@ impl RollupChannel { .execute_optimistic_block_stream(out_stream) .await .map(tonic::Response::into_inner) - // TODO: Don't quietly swallow this error. Provide some form of - // logging. + .inspect_err(|error| warn!(%error, "request to open execute optimistic block stream failed")) .ok()?; Some(InnerExecuteOptimisticBlockStream { inner, }) } + .instrument(info_span!("request execute optimistic block stream")) }) .boxed(); diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 892995dd03..29a5f9fa5f 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -33,6 +33,11 @@ use futures::{ StreamExt as _, }; use prost::Name; +use tracing::{ + info_span, + warn, + Instrument as _, +}; use crate::{ block::Commitment, @@ -92,14 +97,16 @@ impl SequencerChannel { let inner = OptimisticBlockServiceClient::new(chan) .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) .await - // TODO: Don't quietly swallow this error. Provide some form of - // logging. + .inspect_err( + |error| warn!(%error, "request to open block commitment stream failed"), + ) .ok()? .into_inner(); Some(InnerBlockCommitmentStream { inner, }) } + .instrument(info_span!("request block commitment stream")) }) .boxed(); BlockCommitmentStream { @@ -126,14 +133,16 @@ impl SequencerChannel { rollup_id: Some(rollup_id.into_raw()), }) .await - // TODO: Don't quietly swallow this error. Provide some form of - // logging. + .inspect_err( + |error| warn!(%error, "request to open optimistic block stream failed"), + ) .ok()? .into_inner(); Some(InnerOptimisticBlockStream { inner, }) } + .instrument(info_span!("request optimistic block stream")) }) .boxed(); OptimisticBlockStream { diff --git a/crates/astria-auctioneer/src/streaming_utils.rs b/crates/astria-auctioneer/src/streaming_utils.rs index abb8bbc572..bbf8dce7e0 100644 --- a/crates/astria-auctioneer/src/streaming_utils.rs +++ b/crates/astria-auctioneer/src/streaming_utils.rs @@ -50,6 +50,7 @@ pub(crate) fn make_instrumented_channel(uri: &str) -> eyre::Result Date: Mon, 2 Dec 2024 18:37:43 +0100 Subject: [PATCH 57/73] REMOVE FROM PR: emit span events in tracing subscriber --- crates/astria-telemetry/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/astria-telemetry/src/lib.rs b/crates/astria-telemetry/src/lib.rs index 4028a513dd..e5daca4878 100644 --- a/crates/astria-telemetry/src/lib.rs +++ b/crates/astria-telemetry/src/lib.rs @@ -34,6 +34,7 @@ use tracing_subscriber::{ LevelFilter, ParseError, }, + fmt::format::FmtSpan, layer::SubscriberExt as _, util::{ SubscriberInitExt as _, @@ -229,7 +230,11 @@ impl Config { let mut pretty_printer = None; if force_stdout || std::io::stdout().is_terminal() { if pretty_print { - pretty_printer = Some(tracing_subscriber::fmt::layer().compact()); + pretty_printer = Some( + tracing_subscriber::fmt::layer() + .compact() + .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE), + ) } else { tracer_provider = tracer_provider.with_simple_exporter( SpanExporter::builder() From a940b2ebf48b87f062db9c51718aa4840cd77abb Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 3 Dec 2024 12:39:41 +0100 Subject: [PATCH 58/73] add astria-auctioneer to shortnames --- justfile | 1 + 1 file changed, 1 insertion(+) diff --git a/justfile b/justfile index 6c09ebd06e..d94822eb81 100644 --- a/justfile +++ b/justfile @@ -40,6 +40,7 @@ _crate_short_name crate quiet="": #!/usr/bin/env sh set -eu case {{crate}} in + astria-auctioneer) short_name=auctioneer ;; astria-bridge-withdrawer) short_name=bridge-withdrawer ;; astria-cli) short_name=cli ;; astria-composer) short_name=composer ;; From 739746fafa202ebe995994c54f65a216e1c6c202 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Tue, 3 Dec 2024 17:49:41 +0100 Subject: [PATCH 59/73] update all imports to use optimistic, not optimisticblock --- crates/astria-auctioneer/src/block/mod.rs | 4 ++-- crates/astria-auctioneer/src/bundle/mod.rs | 2 +- crates/astria-auctioneer/src/rollup_channel.rs | 10 +++++----- crates/astria-auctioneer/src/sequencer_channel.rs | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index cc1758e3ae..f736a4fdfa 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -1,9 +1,9 @@ use astria_core::{ execution, - generated::{ + generated::astria::{ bundle::v1alpha1 as raw_bundle, sequencerblock::{ - optimisticblock::v1alpha1 as raw_optimistic_block, + optimistic::v1alpha1 as raw_optimistic_block, v1 as raw_sequencer_block, }, }, diff --git a/crates/astria-auctioneer/src/bundle/mod.rs b/crates/astria-auctioneer/src/bundle/mod.rs index 5c3dbdd032..ace2c5cf78 100644 --- a/crates/astria-auctioneer/src/bundle/mod.rs +++ b/crates/astria-auctioneer/src/bundle/mod.rs @@ -3,7 +3,7 @@ use astria_core::{ Signature, VerificationKey, }, - generated::bundle::v1alpha1 as raw, + generated::astria::bundle::v1alpha1 as raw, primitive::v1::{ asset, RollupId, diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 74626ec918..97f7120139 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -7,7 +7,7 @@ use std::{ }, }; -use astria_core::generated::bundle::v1alpha1::{ +use astria_core::generated::astria::bundle::v1alpha1::{ bundle_service_client::BundleServiceClient, BaseBlock, ExecuteOptimisticBlockStreamResponse, @@ -59,7 +59,7 @@ impl RollupChannel { } pub(crate) fn open_bundle_stream(&self) -> BundleStream { - use astria_core::generated::bundle::v1alpha1::GetBundleStreamRequest; + use astria_core::generated::astria::bundle::v1alpha1::GetBundleStreamRequest; let chan = self.inner.clone(); let inner = restarting_stream(move || { let chan = chan.clone(); @@ -83,7 +83,7 @@ impl RollupChannel { } pub(crate) fn open_execute_optimistic_block_stream(&self) -> ExecuteOptimisticBlockStream { - use astria_core::generated::bundle::v1alpha1::{ + use astria_core::generated::astria::bundle::v1alpha1::{ optimistic_execution_service_client::OptimisticExecutionServiceClient, ExecuteOptimisticBlockStreamRequest, }; @@ -165,7 +165,7 @@ impl Stream for InnerBundleStream { let bundle = Bundle::try_from_raw(raw).wrap_err_with(|| { format!( "failed to validate received message `{}`", - astria_core::generated::bundle::v1alpha1::Bundle::full_name() + astria_core::generated::astria::bundle::v1alpha1::Bundle::full_name() ) })?; @@ -215,7 +215,7 @@ impl Stream for InnerExecuteOptimisticBlockStream { let executed_block = crate::block::Executed::try_from_raw(message).wrap_err_with(|| { format!( "failed to validate `{}`", - astria_core::generated::bundle::v1alpha1::ExecuteOptimisticBlockStreamResponse::full_name(), + astria_core::generated::astria::bundle::v1alpha1::ExecuteOptimisticBlockStreamResponse::full_name(), ) })?; Poll::Ready(Some(Ok(executed_block))) diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 29a5f9fa5f..5c7641996e 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -8,7 +8,7 @@ use std::{ }; use astria_core::{ - generated::sequencerblock::optimisticblock::v1alpha1::{ + generated::astria::sequencerblock::optimistic::v1alpha1::{ GetBlockCommitmentStreamRequest, GetBlockCommitmentStreamResponse, GetOptimisticBlockStreamRequest, @@ -68,7 +68,7 @@ impl SequencerChannel { &self, address: Address, ) -> impl Future> { - use astria_core::generated::sequencerblock::v1::{ + use astria_core::generated::astria::sequencerblock::v1::{ sequencer_service_client::SequencerServiceClient, GetPendingNonceRequest, }; @@ -88,7 +88,7 @@ impl SequencerChannel { } pub(crate) fn open_get_block_commitment_stream(&self) -> BlockCommitmentStream { - use astria_core::generated::sequencerblock::optimisticblock::v1alpha1:: + use astria_core::generated::astria::sequencerblock::optimistic::v1alpha1:: optimistic_block_service_client::OptimisticBlockServiceClient; let chan = self.inner.clone(); let inner = restarting_stream(move || { @@ -118,7 +118,7 @@ impl SequencerChannel { &self, rollup_id: RollupId, ) -> OptimisticBlockStream { - use astria_core::generated::sequencerblock::optimisticblock::v1alpha1::{ + use astria_core::generated::astria::sequencerblock::optimistic::v1alpha1::{ optimistic_block_service_client::OptimisticBlockServiceClient, GetOptimisticBlockStreamRequest, }; @@ -172,7 +172,7 @@ impl Stream for InnerBlockCommitmentStream { type Item = eyre::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - use astria_core::generated::sequencerblock::optimisticblock::v1alpha1 as raw; + use astria_core::generated::astria::sequencerblock::optimistic::v1alpha1 as raw; let Some(res) = std::task::ready!(self.inner.poll_next_unpin(cx)) else { return Poll::Ready(None); From 78fa64675b87a4a1b4b47528d5e89529efd01638 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 06:45:20 +0100 Subject: [PATCH 60/73] unset running auction after completion --- crates/astria-auctioneer/src/auctioneer/inner/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 23448015a3..3dbbea48f6 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -198,10 +198,11 @@ impl Inner { /// This method only exists to emit the auction result (only error right now) under a span. #[instrument(skip_all, fields(%auction_id), err)] fn handle_completed_auction( - &self, + &mut self, auction_id: auction::Id, res: eyre::Result<()>, ) -> eyre::Result<()> { + self.running_auction.take(); res } From b074a8b9b3f044f29476d5f0c7b41e532016cda6 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 10:04:45 +0100 Subject: [PATCH 61/73] return the connection error in restarting stream it was only emitted inside the future constructed by the closure used to start the reconnection. Returning it means a downstream user can explicitly log it. The next invocation of RestartingStream::next will then return Ready(None). --- .../astria-auctioneer/src/rollup_channel.rs | 16 ++++++------ .../src/sequencer_channel.rs | 16 +++++------- .../astria-auctioneer/src/streaming_utils.rs | 25 ++++++++++--------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/crates/astria-auctioneer/src/rollup_channel.rs b/crates/astria-auctioneer/src/rollup_channel.rs index 97f7120139..e7a1b7fa3d 100644 --- a/crates/astria-auctioneer/src/rollup_channel.rs +++ b/crates/astria-auctioneer/src/rollup_channel.rs @@ -67,10 +67,10 @@ impl RollupChannel { let inner = BundleServiceClient::new(chan) .get_bundle_stream(GetBundleStreamRequest {}) .await - .map(tonic::Response::into_inner) - .inspect_err(|error| warn!(%error, "request to open bundle stream failed")) - .ok()?; - Some(InnerBundleStream { + .wrap_err("failed to open bundle stream") + .inspect_err(|error| warn!(%error))? + .into_inner(); + Ok(InnerBundleStream { inner, }) } @@ -110,10 +110,10 @@ impl RollupChannel { let inner = OptimisticExecutionServiceClient::new(chan) .execute_optimistic_block_stream(out_stream) .await - .map(tonic::Response::into_inner) - .inspect_err(|error| warn!(%error, "request to open execute optimistic block stream failed")) - .ok()?; - Some(InnerExecuteOptimisticBlockStream { + .wrap_err("failed to open execute optimistic block stream") + .inspect_err(|error| warn!(%error))? + .into_inner(); + Ok(InnerExecuteOptimisticBlockStream { inner, }) } diff --git a/crates/astria-auctioneer/src/sequencer_channel.rs b/crates/astria-auctioneer/src/sequencer_channel.rs index 5c7641996e..c212d1f27a 100644 --- a/crates/astria-auctioneer/src/sequencer_channel.rs +++ b/crates/astria-auctioneer/src/sequencer_channel.rs @@ -97,12 +97,10 @@ impl SequencerChannel { let inner = OptimisticBlockServiceClient::new(chan) .get_block_commitment_stream(GetBlockCommitmentStreamRequest {}) .await - .inspect_err( - |error| warn!(%error, "request to open block commitment stream failed"), - ) - .ok()? + .wrap_err("failed to open block commitment stream") + .inspect_err(|error| warn!(%error))? .into_inner(); - Some(InnerBlockCommitmentStream { + Ok(InnerBlockCommitmentStream { inner, }) } @@ -133,12 +131,10 @@ impl SequencerChannel { rollup_id: Some(rollup_id.into_raw()), }) .await - .inspect_err( - |error| warn!(%error, "request to open optimistic block stream failed"), - ) - .ok()? + .wrap_err("failed to open optimistic block stream") + .inspect_err(|error| warn!(%error))? .into_inner(); - Some(InnerOptimisticBlockStream { + Ok(InnerOptimisticBlockStream { inner, }) } diff --git a/crates/astria-auctioneer/src/streaming_utils.rs b/crates/astria-auctioneer/src/streaming_utils.rs index bbf8dce7e0..514ff7458b 100644 --- a/crates/astria-auctioneer/src/streaming_utils.rs +++ b/crates/astria-auctioneer/src/streaming_utils.rs @@ -1,4 +1,5 @@ use std::{ + marker::PhantomData, pin::Pin, task::{ ready, @@ -63,17 +64,18 @@ pub(crate) fn make_instrumented_channel(uri: &str) -> eyre::Result(f: F) -> RestartingStream +pub(crate) fn restarting_stream(f: F) -> RestartingStream where F: Fn() -> Fut, - Fut: Future>, - S: Stream, + Fut: Future>, + S: Stream>, { let opening_stream = Some(f()); RestartingStream { f, opening_stream, running_stream: None, + _phantom_data: PhantomData, } } @@ -82,20 +84,21 @@ where // Specifically explain why Fut returns Option, and how to return // an error to the user (tracing). pin_project! { - pub(crate) struct RestartingStream { + pub(crate) struct RestartingStream { f: F, #[pin] opening_stream: Option, #[pin] running_stream: Option, + _phantom_data: PhantomData>, } } -impl Stream for RestartingStream +impl Stream for RestartingStream where F: Fn() -> Fut, - Fut: Future>, - S: Stream, + Fut: Future>, + S: Stream>, { type Item = S::Item; @@ -109,17 +112,15 @@ where this.opening_stream .as_mut() .as_pin_mut() - .expect("inside a branch that checks for opening_stream == Some") + .expect("inside a branch that checks opening_stream == Some") .poll_unpin(cx) ); // The future has completed, unset it so it will not be polled again. Pin::set(&mut this.opening_stream, None); match open_output { - Some(stream) => { - Pin::set(&mut this.running_stream, Some(stream)); - } - None => return Poll::Ready(None), + Ok(stream) => Pin::set(&mut this.running_stream, Some(stream)), + Err(err) => return Poll::Ready(Some(Err(err))), } } From 9c6f9261d50260844fc63492b899e990a99f42b5 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 15:28:33 +0100 Subject: [PATCH 62/73] fix allocation rule never taking bids with zero-bids --- .../auctioneer/inner/auction/allocation_rule.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs index 8b9e4fb05b..f0c4d7c304 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs @@ -16,12 +16,16 @@ impl FirstPrice { /// Submit a bundle with a bid. /// /// Returns `true` if the bid is accepted as the highest bid. - pub(super) fn bid(&mut self, bundle: Bundle) -> bool { - if bundle.bid() > self.highest_bid.as_ref().map_or(0, Bundle::bid) { - self.highest_bid = Some(bundle); - true + pub(super) fn bid(&mut self, candidate: Bundle) -> bool { + if let Some(current) = self.highest_bid.as_mut() { + let is_higher = candidate.bid() > current.bid(); + if is_higher { + *current = candidate; + } + is_higher } else { - false + self.highest_bid = Some(candidate); + true } } From 070a9e7627626bec2aa38bd1045e2e870bcb8e66 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 15:51:10 +0100 Subject: [PATCH 63/73] use Arcs when passing bundles to running auction; instrument first price mechanism --- .../inner/auction/allocation_rule.rs | 35 ++++++++++++------ .../src/auctioneer/inner/auction/factory.rs | 2 +- .../src/auctioneer/inner/auction/mod.rs | 6 ++- .../src/auctioneer/inner/auction/worker.rs | 37 +++++++------------ .../src/auctioneer/inner/mod.rs | 7 +++- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs index f0c4d7c304..7d1db4af0f 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs @@ -1,9 +1,16 @@ //! The allocation rule is the mechanism by which the auction processes incoming bids and determines //! the winner. +use std::sync::Arc; + +use tracing::{ + info, + instrument, +}; + use super::Bundle; pub(super) struct FirstPrice { - highest_bid: Option, + highest_bid: Option>, } impl FirstPrice { @@ -16,21 +23,27 @@ impl FirstPrice { /// Submit a bundle with a bid. /// /// Returns `true` if the bid is accepted as the highest bid. - pub(super) fn bid(&mut self, candidate: Bundle) -> bool { - if let Some(current) = self.highest_bid.as_mut() { - let is_higher = candidate.bid() > current.bid(); - if is_higher { - *current = candidate; + #[instrument(skip_all, fields( + current_winner.bid = self.highest_bid.as_ref().map(|bundle| bundle.bid()), + candidate.bid = candidate.bid(), + ))] + pub(super) fn bid(&mut self, candidate: &Arc) { + let winner = if let Some(current) = self.highest_bid.as_mut() { + if candidate.bid() > current.bid() { + *current = candidate.clone(); + "candidate" + } else { + "incumbant" } - is_higher } else { - self.highest_bid = Some(candidate); - true - } + self.highest_bid = Some(candidate.clone()); + "candidate" + }; + info!("highest bidder is {winner}"); } /// Returns the winner of the auction, if one exists. - pub(super) fn winner(self) -> Option { + pub(super) fn winner(self) -> Option> { self.highest_bid } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index f1f50bc601..0f71933115 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -44,7 +44,7 @@ impl Factory { let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), commands_rx, - bundles_rx, + bundles: bundles_rx, latency_margin: self.latency_margin, id, sequencer_key: self.sequencer_key.clone(), diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 29f2ef35c4..8bff63b874 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -37,6 +37,8 @@ //! as it received the signal to start the timer. This corresponds to the sequencer block being //! committed, thus providing the latest pending nonce. +use std::sync::Arc; + use astria_core::{ self, sequencerblock::v1::block::BlockHash, @@ -101,7 +103,7 @@ pub(super) struct Auction { height: u64, parent_block_of_executed: Option<[u8; 32]>, commands: mpsc::Sender, - bundles: mpsc::Sender, + bundles: mpsc::Sender>, worker: JoinHandle>, } @@ -160,7 +162,7 @@ impl Auction { ), err)] pub(in crate::auctioneer::inner) fn forward_bundle_to_auction( &mut self, - bundle: Bundle, + bundle: Arc, ) -> eyre::Result<()> { // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, // tacked block hash, provided block hash, etc. diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index e4d4ee9f04..7ed67285dd 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -37,7 +37,10 @@ //! as it received the signal to start the timer. This corresponds to the sequencer block being //! committed, thus providing the latest pending nonce. -use std::time::Duration; +use std::{ + sync::Arc, + time::Duration, +}; use astria_core::primitive::v1::{ asset, @@ -55,7 +58,6 @@ use tokio::{ sync::mpsc, }; use tracing::{ - debug, error, info, instrument, @@ -77,7 +79,7 @@ pub(super) struct Worker { /// Channel for receiving commands sent via the handle pub(super) commands_rx: mpsc::Receiver, /// Channel for receiving new bundles - pub(super) bundles_rx: mpsc::Receiver, + pub(super) bundles: mpsc::Receiver>, /// The time between receiving a block commitment pub(super) latency_margin: Duration, /// The ID of the auction @@ -129,38 +131,27 @@ impl Worker { } } - Some(bundle) = self.bundles_rx.recv(), if auction_is_open => { - if allocation_rule.bid(bundle.clone()) { - info!( - auction.id = %self.id, - bundle.bid = %bundle.bid(), - "received new highest bid" - ); - } else { - debug!( - auction.id = %self.id, - bundle.bid = %bundle.bid(), - "received bid lower than current highest bid, discarding" - ); - } + Some(bundle) = self.bundles.recv(), if auction_is_open => { + allocation_rule.bid(&bundle); } } }; - // TODO: report the pending nonce that we ended up using. - let transaction_body = auction_result + let winner = auction_result .wrap_err("auction failed unexpectedly")? - .ok_or_eyre("auction ended with no winning bid")? + .ok_or_eyre("auction ended with no winning bid")?; + + // TODO: report the pending nonce that we ended up using. + let transaction = Arc::unwrap_or_clone(winner) .into_transaction_body( self.pending_nonce.get(), self.rollup_id, &self.sequencer_key, self.fee_asset_denomination.clone(), self.sequencer_chain_id, - ); - - let transaction = transaction_body.sign(self.sequencer_key.signing_key()); + ) + .sign(self.sequencer_key.signing_key()); // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, // it's likey lost. diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 3dbbea48f6..4a0c68a808 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -1,4 +1,7 @@ -use std::time::Duration; +use std::{ + sync::Arc, + time::Duration, +}; use astria_core::{ primitive::v1::{ @@ -301,7 +304,7 @@ impl Inner { #[instrument(skip(self), fields(block_hash = field::Empty), err)] fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { - let bundle = bundle.wrap_err("received problematic bundle")?; + let bundle = Arc::new(bundle.wrap_err("received problematic bundle")?); Span::current().record( "block_hash", field::display(bundle.base_sequencer_block_hash()), From 6bff3e5fff60bc835cb0bfe380df5dc8b386c4b5 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 16:08:16 +0100 Subject: [PATCH 64/73] make the auction bundle channel unbounded Auctions are very short lived tasks: it does not matter if their channels are bounded or not. After a short period they will end and their memory freed. --- .../src/auctioneer/inner/auction/allocation_rule.rs | 1 + .../src/auctioneer/inner/auction/factory.rs | 2 +- .../astria-auctioneer/src/auctioneer/inner/auction/mod.rs | 6 +++--- .../src/auctioneer/inner/auction/worker.rs | 5 ++++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs index 7d1db4af0f..0cc3475dbe 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/allocation_rule.rs @@ -23,6 +23,7 @@ impl FirstPrice { /// Submit a bundle with a bid. /// /// Returns `true` if the bid is accepted as the highest bid. + // TODO: identify the incumbant and candidate by their hash? #[instrument(skip_all, fields( current_winner.bid = self.highest_bid.as_ref().map(|bundle| bundle.bid()), candidate.bid = candidate.bid(), diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 0f71933115..9bfc8d264e 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -39,7 +39,7 @@ impl Factory { // TODO: get the capacities from config or something instead of using a magic number let (commands_tx, commands_rx) = mpsc::channel(16); - let (bundles_tx, bundles_rx) = mpsc::channel(16); + let (bundles_tx, bundles_rx) = mpsc::unbounded_channel(); let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 8bff63b874..f8fb8daa7f 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -103,7 +103,7 @@ pub(super) struct Auction { height: u64, parent_block_of_executed: Option<[u8; 32]>, commands: mpsc::Sender, - bundles: mpsc::Sender>, + bundles: mpsc::UnboundedSender>, worker: JoinHandle>, } @@ -183,8 +183,8 @@ impl Auction { base64(bundle.parent_rollup_block_hash()), ); self.bundles - .try_send(bundle) - .wrap_err("failed to submit bundle to auction") + .send(bundle) + .wrap_err("failed to submit bundle to auction; the bundle is lost") } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index 7ed67285dd..f5cb2c511a 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -79,7 +79,7 @@ pub(super) struct Worker { /// Channel for receiving commands sent via the handle pub(super) commands_rx: mpsc::Receiver, /// Channel for receiving new bundles - pub(super) bundles: mpsc::Receiver>, + pub(super) bundles: tokio::sync::mpsc::UnboundedReceiver>, /// The time between receiving a block commitment pub(super) latency_margin: Duration, /// The ID of the auction @@ -109,6 +109,7 @@ impl Worker { // get the auction winner when the timer expires _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { + info!("timer is up; bids left unprocessed: {}", self.bundles.len()); break Ok(allocation_rule.winner()); } @@ -131,6 +132,8 @@ impl Worker { } } + // TODO: this is an unbounded channel. Can we process multiple + // bids at a time? Some(bundle) = self.bundles.recv(), if auction_is_open => { allocation_rule.bid(&bundle); } From 5d66615d4cad23c9c609cdde8cebc06f2767f6ab Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 17:28:09 +0100 Subject: [PATCH 65/73] replace the mpsc command channel by two oneshot channels to trigger auction steps the oneshot channels communicate cleanly what's happening inside the auction and it's easier to track what was already sent to the auction worker (to reject even triggering other steps on the worker from in its handle). --- .../src/auctioneer/inner/auction/factory.rs | 14 ++++-- .../src/auctioneer/inner/auction/mod.rs | 47 +++++++++++++------ .../src/auctioneer/inner/auction/worker.rs | 43 +++++++++-------- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 9bfc8d264e..9fb9b0a5a3 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -7,7 +7,10 @@ use astria_core::{ }, sequencerblock::v1::block::FilteredSequencerBlock, }; -use tokio::sync::mpsc; +use tokio::sync::{ + mpsc, + oneshot, +}; use super::{ Auction, @@ -38,12 +41,14 @@ impl Factory { let height = block.height().into(); // TODO: get the capacities from config or something instead of using a magic number - let (commands_tx, commands_rx) = mpsc::channel(16); + let (start_bids_tx, start_bids_rx) = oneshot::channel(); + let (start_timer_tx, start_timer_rx) = oneshot::channel(); let (bundles_tx, bundles_rx) = mpsc::unbounded_channel(); let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), - commands_rx, + start_processing_bids: Some(start_bids_rx), + start_timer: Some(start_timer_rx), bundles: bundles_rx, latency_margin: self.latency_margin, id, @@ -59,7 +64,8 @@ impl Factory { block_hash, height, parent_block_of_executed: None, - commands: commands_tx, + start_bids: Some(start_bids_tx), + start_timer: Some(start_timer_tx), bundles: bundles_tx, worker: tokio::task::spawn(auction.run()), } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index f8fb8daa7f..d0e3f56479 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -45,7 +45,9 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, + bail, ensure, + eyre, Context, }; use futures::{ @@ -54,7 +56,10 @@ use futures::{ }; use telemetry::display::base64; use tokio::{ - sync::mpsc, + sync::{ + mpsc, + oneshot, + }, task::JoinHandle, }; use tracing::instrument; @@ -92,17 +97,13 @@ impl std::fmt::Display for Id { } } -enum Command { - StartProcessingBids, - StartTimer, -} - pub(super) struct Auction { id: Id, block_hash: BlockHash, height: u64, parent_block_of_executed: Option<[u8; 32]>, - commands: mpsc::Sender, + start_bids: Option>, + start_timer: Option>, bundles: mpsc::UnboundedSender>, worker: JoinHandle>, } @@ -116,6 +117,7 @@ impl Auction { &self.id } + // TODO: identify the commitment in span fields #[instrument(skip_all, fields(id = %self.id), err)] pub(super) fn start_timer(&mut self, commitment: Commitment) -> eyre::Result<()> { ensure!( @@ -127,11 +129,18 @@ impl Auction { commitment.block_hash(), commitment.height(), ); - self.commands - .try_send(Command::StartTimer) - .wrap_err("failed to send command to start timer to auction") + if let Some(start_timer) = self.start_timer.take() { + start_timer + .send(()) + .map_err(|()| eyre!("the auction worker's start timer channel was already dropped")) + } else { + Err(eyre!( + "a previous commitment already triggered the start timer of the auction" + )) + } } + // TODO: identify the executed block in the span fields #[instrument(skip_all, fields(id = %self.id), err)] pub(in crate::auctioneer::inner) fn start_processing_bids( &mut self, @@ -144,13 +153,21 @@ impl Auction { &self.block_hash, block.sequencer_block_hash(), ); - // TODO: What if it was already set? Overwrite? Replace? Drop? - let _ = self + + if let Some(start_bids) = self.start_bids.take() { + start_bids.send(()).map_err(|()| { + eyre!("the auction worker's start bids channel was already dropped") + })?; + } else { + bail!("a previous executed block already triggered the auction to start bids"); + } + + let _prev_block = self .parent_block_of_executed .replace(block.parent_rollup_block_hash()); - self.commands - .try_send(Command::StartProcessingBids) - .wrap_err("failed to send command to start processing bids") + debug_assert!(_prev_block.is_some()); + + Ok(()) } // TODO: Use a refinement type for the parente rollup block hash diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index f5cb2c511a..fa27c84926 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -48,6 +48,7 @@ use astria_core::primitive::v1::{ }; use astria_eyre::eyre::{ self, + bail, eyre, Context, OptionExt as _, @@ -55,7 +56,7 @@ use astria_eyre::eyre::{ use sequencer_client::SequencerClientExt; use tokio::{ select, - sync::mpsc, + sync::oneshot, }; use tracing::{ error, @@ -65,7 +66,6 @@ use tracing::{ use super::{ allocation_rule::FirstPrice, - Command, PendingNonceSubscriber, }; use crate::{ @@ -76,8 +76,8 @@ use crate::{ pub(super) struct Worker { /// The sequencer's ABCI client, used for submitting transactions pub(super) sequencer_abci_client: sequencer_client::HttpClient, - /// Channel for receiving commands sent via the handle - pub(super) commands_rx: mpsc::Receiver, + pub(super) start_processing_bids: Option>, + pub(super) start_timer: Option>, /// Channel for receiving new bundles pub(super) bundles: tokio::sync::mpsc::UnboundedReceiver>, /// The time between receiving a block commitment @@ -107,37 +107,36 @@ impl Worker { select! { biased; - // get the auction winner when the timer expires _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { info!("timer is up; bids left unprocessed: {}", self.bundles.len()); break Ok(allocation_rule.winner()); } - Some(cmd) = self.commands_rx.recv() => { - match cmd { - Command::StartProcessingBids => { - if auction_is_open { - break Err(eyre!("auction received signal to start processing bids twice")); - } - auction_is_open = true; - }, - Command::StartTimer => { - if !auction_is_open { - break Err(eyre!("auction received signal to start timer before signal to start processing bids")); - } + Ok(()) = async { self.start_processing_bids.as_mut().unwrap().await }, if self.start_processing_bids.is_some() => { + let mut channel = self.start_processing_bids.take().expect("inside an arm that that checks start_processing_bids == Some"); + channel.close(); + auction_is_open = true; + } - // set the timer - latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); - } + Ok(()) = async { self.start_timer.as_mut().unwrap().await }, if self.start_timer.is_some() => { + let mut channel = self.start_timer.take().expect("inside an arm that checks start_immer == Some"); + channel.close(); + if !auction_is_open { + break Err(eyre!("auction received signal to start timer before signal to start processing bids")); } + + // set the timer + latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); } - // TODO: this is an unbounded channel. Can we process multiple - // bids at a time? + // TODO: this is an unbounded channel. Can we process multiple bids at a time? Some(bundle) = self.bundles.recv(), if auction_is_open => { allocation_rule.bid(&bundle); } + else => { + bail!("all channels are closed; this auction cannot continue") + } } }; From f6ee3a558afb20f33a89bc5ddc4b565b05b2916c Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 17:35:24 +0100 Subject: [PATCH 66/73] separate the trigger to start the auction timer from the bids the trigger to start the auction timer comes from the sequencer block commitment. the trigger to start bids comes from the rollup executing the optimistic block in time. this means that the timer must start running independently of whether or not the rollup returned with the the executed block in time. --- .../src/auctioneer/inner/auction/worker.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index fa27c84926..26a43ee0d5 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -49,7 +49,6 @@ use astria_core::primitive::v1::{ use astria_eyre::eyre::{ self, bail, - eyre, Context, OptionExt as _, }; @@ -109,7 +108,7 @@ impl Worker { _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { info!("timer is up; bids left unprocessed: {}", self.bundles.len()); - break Ok(allocation_rule.winner()); + break allocation_rule.winner(); } Ok(()) = async { self.start_processing_bids.as_mut().unwrap().await }, if self.start_processing_bids.is_some() => { @@ -122,10 +121,10 @@ impl Worker { let mut channel = self.start_timer.take().expect("inside an arm that checks start_immer == Some"); channel.close(); if !auction_is_open { - break Err(eyre!("auction received signal to start timer before signal to start processing bids")); + info!("received signal to start the auction timer before signal to process bids; that's ok but eats into the time allotment of the auction"); } - // set the timer + // TODO: Emit an event to report start and endpoint of the auction. latency_margin_timer = Some(tokio::time::sleep(self.latency_margin)); } @@ -140,9 +139,7 @@ impl Worker { } }; - let winner = auction_result - .wrap_err("auction failed unexpectedly")? - .ok_or_eyre("auction ended with no winning bid")?; + let winner = auction_result.ok_or_eyre("auction ended with no winning bid")?; // TODO: report the pending nonce that we ended up using. let transaction = Arc::unwrap_or_clone(winner) From 2ac6504dffff90dc5a078ccd46c734333022d8e8 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 17:38:01 +0100 Subject: [PATCH 67/73] small refactor: start_processing_bids -> start_bids --- .../src/auctioneer/inner/auction/factory.rs | 2 +- .../astria-auctioneer/src/auctioneer/inner/auction/mod.rs | 2 +- .../src/auctioneer/inner/auction/worker.rs | 6 +++--- crates/astria-auctioneer/src/auctioneer/inner/mod.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 9fb9b0a5a3..959c5c75d0 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -47,7 +47,7 @@ impl Factory { let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), - start_processing_bids: Some(start_bids_rx), + start_bids: Some(start_bids_rx), start_timer: Some(start_timer_rx), bundles: bundles_rx, latency_margin: self.latency_margin, diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index d0e3f56479..c6d6b9e9f4 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -142,7 +142,7 @@ impl Auction { // TODO: identify the executed block in the span fields #[instrument(skip_all, fields(id = %self.id), err)] - pub(in crate::auctioneer::inner) fn start_processing_bids( + pub(in crate::auctioneer::inner) fn start_bids( &mut self, block: crate::block::Executed, ) -> eyre::Result<()> { diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index 26a43ee0d5..e82ad90191 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -75,7 +75,7 @@ use crate::{ pub(super) struct Worker { /// The sequencer's ABCI client, used for submitting transactions pub(super) sequencer_abci_client: sequencer_client::HttpClient, - pub(super) start_processing_bids: Option>, + pub(super) start_bids: Option>, pub(super) start_timer: Option>, /// Channel for receiving new bundles pub(super) bundles: tokio::sync::mpsc::UnboundedReceiver>, @@ -111,8 +111,8 @@ impl Worker { break allocation_rule.winner(); } - Ok(()) = async { self.start_processing_bids.as_mut().unwrap().await }, if self.start_processing_bids.is_some() => { - let mut channel = self.start_processing_bids.take().expect("inside an arm that that checks start_processing_bids == Some"); + Ok(()) = async { self.start_bids.as_mut().unwrap().await }, if self.start_bids.is_some() => { + let mut channel = self.start_bids.take().expect("inside an arm that that checks start_bids == Some"); channel.close(); auction_is_open = true; } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 4a0c68a808..8f1631892d 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -287,7 +287,7 @@ impl Inner { ); if let Some(running_auction) = &mut self.running_auction { running_auction - .start_processing_bids(executed_block) + .start_bids(executed_block) .wrap_err("failed to start processing bids")?; info!( auction_id = %running_auction.id(), From 0a8a59575acbb0a4fcf456f781ef65261c7eb3b1 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 17:39:29 +0100 Subject: [PATCH 68/73] add some todos to the auction --- crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index e82ad90191..c76286e6da 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -114,6 +114,7 @@ impl Worker { Ok(()) = async { self.start_bids.as_mut().unwrap().await }, if self.start_bids.is_some() => { let mut channel = self.start_bids.take().expect("inside an arm that that checks start_bids == Some"); channel.close(); + // TODO: if the timer is already running, report how much time is left for the bids auction_is_open = true; } From c4ea20394dbecd92fa2f32e92f11d479601ca389 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 19:32:58 +0100 Subject: [PATCH 69/73] not receiving winning bids does not constitute an error for auctioneer: just log and return --- .../astria-auctioneer/src/auctioneer/inner/auction/worker.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index c76286e6da..7f1bd5c179 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -140,7 +140,10 @@ impl Worker { } }; - let winner = auction_result.ok_or_eyre("auction ended with no winning bid")?; + let Some(winner) = auction_result else { + info!("auction ended with no winning bid"); + return Ok(()); + }; // TODO: report the pending nonce that we ended up using. let transaction = Arc::unwrap_or_clone(winner) From 82e401af3150090a54ed96cc7cd8388aab92af9c Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 4 Dec 2024 19:37:08 +0100 Subject: [PATCH 70/73] skip_all not only skip(self) --- crates/astria-auctioneer/src/auctioneer/inner/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 8f1631892d..986dabfe81 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -221,7 +221,7 @@ impl Inner { res } - #[instrument(skip(self), fields(block_hash = field::Empty), err)] + #[instrument(skip_all, fields(block_hash = field::Empty), err)] fn handle_optimistic_block( &mut self, optimistic_block: eyre::Result, @@ -252,7 +252,7 @@ impl Inner { Ok(()) } - #[instrument(skip(self), fields(block_hash = field::Empty), err)] + #[instrument(skip_all, fields(block_hash = field::Empty), err)] fn handle_block_commitment( &mut self, commitment: eyre::Result, @@ -275,7 +275,7 @@ impl Inner { Ok(()) } - #[instrument(skip(self), fields(block_hash = field::Empty), err)] + #[instrument(skip_all, fields(block_hash = field::Empty), err)] fn handle_executed_block( &mut self, executed_block: eyre::Result, @@ -302,7 +302,7 @@ impl Inner { Ok(()) } - #[instrument(skip(self), fields(block_hash = field::Empty), err)] + #[instrument(skip_all, fields(block_hash = field::Empty), err)] fn handle_bundle(&mut self, bundle: eyre::Result) -> eyre::Result<()> { let bundle = Arc::new(bundle.wrap_err("received problematic bundle")?); Span::current().record( From 779b36fba6282b444297afaefd1ef5b6d105f175 Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 11 Dec 2024 20:56:57 +0100 Subject: [PATCH 71/73] cancel auctions instead of aborting them --- .../src/auctioneer/inner/auction/factory.rs | 7 +- .../src/auctioneer/inner/auction/mod.rs | 44 ++++- .../src/auctioneer/inner/auction/worker.rs | 152 +++++++++++------- .../src/auctioneer/inner/mod.rs | 35 ++-- 4 files changed, 160 insertions(+), 78 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 959c5c75d0..18a5465e79 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -2,8 +2,8 @@ /// associated handles. use astria_core::{ primitive::v1::{ - asset, RollupId, + asset, }, sequencerblock::v1::block::FilteredSequencerBlock, }; @@ -11,6 +11,7 @@ use tokio::sync::{ mpsc, oneshot, }; +use tokio_util::sync::CancellationToken; use super::{ Auction, @@ -29,6 +30,7 @@ pub(in crate::auctioneer::inner) struct Factory { pub(in crate::auctioneer::inner) sequencer_chain_id: String, pub(in crate::auctioneer::inner) rollup_id: RollupId, pub(in crate::auctioneer::inner) pending_nonce: PendingNonceSubscriber, + pub(in crate::auctioneer::inner) cancellation_token: CancellationToken, } impl Factory { @@ -45,6 +47,7 @@ impl Factory { let (start_timer_tx, start_timer_rx) = oneshot::channel(); let (bundles_tx, bundles_rx) = mpsc::unbounded_channel(); + let cancellation_token = self.cancellation_token.child_token(); let auction = Worker { sequencer_abci_client: self.sequencer_abci_client.clone(), start_bids: Some(start_bids_rx), @@ -57,6 +60,7 @@ impl Factory { sequencer_chain_id: self.sequencer_chain_id.clone(), rollup_id: self.rollup_id, pending_nonce: self.pending_nonce.clone(), + cancellation_token: cancellation_token.clone(), }; Auction { @@ -67,6 +71,7 @@ impl Factory { start_bids: Some(start_bids_tx), start_timer: Some(start_timer_tx), bundles: bundles_tx, + cancellation_token, worker: tokio::task::spawn(auction.run()), } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index c6d6b9e9f4..3ca9275ab8 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -37,7 +37,10 @@ //! as it received the signal to start the timer. This corresponds to the sequencer block being //! committed, thus providing the latest pending nonce. -use std::sync::Arc; +use std::{ + fmt::Display, + sync::Arc, +}; use astria_core::{ self, @@ -45,15 +48,16 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, + Context, bail, ensure, eyre, - Context, }; use futures::{ Future, FutureExt as _, }; +use sequencer_client::tendermint_rpc::endpoint::broadcast::tx_sync; use telemetry::display::base64; use tokio::{ sync::{ @@ -62,13 +66,13 @@ use tokio::{ }, task::JoinHandle, }; +use tokio_util::sync::CancellationToken; use tracing::instrument; use super::PendingNonceSubscriber; use crate::{ block::Commitment, bundle::Bundle, - flatten_join_result, sequencer_key::SequencerKey, }; @@ -105,7 +109,8 @@ pub(super) struct Auction { start_bids: Option>, start_timer: Option>, bundles: mpsc::UnboundedSender>, - worker: JoinHandle>, + cancellation_token: CancellationToken, + worker: JoinHandle>, } impl Auction { @@ -113,6 +118,10 @@ impl Auction { self.worker.abort(); } + pub(super) fn cancel(&self) { + self.cancellation_token.cancel() + } + pub(in crate::auctioneer::inner) fn id(&self) -> &Id { &self.id } @@ -206,13 +215,36 @@ impl Auction { } impl Future for Auction { - type Output = (Id, eyre::Result<()>); + type Output = (Id, Result<(), tokio::task::JoinError>); fn poll( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { let res = std::task::ready!(self.worker.poll_unpin(cx)); - std::task::Poll::Ready((self.id, flatten_join_result(res))) + std::task::Poll::Ready((self.id, res.map(|_| ()))) + } +} + +pub(super) enum Summary { + CancelledDuringAuction, + NoBids, + Submitted(tx_sync::Response), +} + +impl Display for Summary { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Summary::CancelledDuringAuction => { + f.write_str("received cancellation signal during auction loop") + } + Summary::NoBids => f.write_str("auction finished without bids"), + Summary::Submitted(rsp) => write!( + f, + "auction winner submitted; Sequencer responed with ABCI code `{}`, log `{}`", + rsp.code.value(), + rsp.log, + ), + } } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index 7f1bd5c179..891c619143 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -43,29 +43,30 @@ use std::{ }; use astria_core::primitive::v1::{ - asset, RollupId, + asset, }; use astria_eyre::eyre::{ self, - bail, Context, - OptionExt as _, + bail, }; use sequencer_client::SequencerClientExt; use tokio::{ select, sync::oneshot, }; +use tokio_util::sync::CancellationToken; use tracing::{ - error, + Level, info, instrument, }; use super::{ - allocation_rule::FirstPrice, PendingNonceSubscriber, + Summary, + allocation_rule::FirstPrice, }; use crate::{ bundle::Bundle, @@ -92,37 +93,119 @@ pub(super) struct Worker { /// Rollup ID to submit the auction result to pub(super) rollup_id: RollupId, pub(super) pending_nonce: PendingNonceSubscriber, + pub(super) cancellation_token: CancellationToken, } impl Worker { - #[instrument(skip_all, fields(id = %self.id))] - pub(super) async fn run(mut self) -> eyre::Result<()> { + // FIXME: consider using Valuable for the return case. + // See this discussion: https://github.com/tokio-rs/tracing/discussions/1906 + #[instrument( + skip_all, + fields(id = %self.id), + err(level = Level::WARN, Display), + ret(Display), + )] + pub(super) async fn run(mut self) -> eyre::Result { + let Some(auction_result) = self + .cancellation_token + .clone() + .run_until_cancelled(self.run_auction_loop()) + .await + else { + return Ok(Summary::CancelledDuringAuction); + }; + let Some(winner) = auction_result.wrap_err("auction failed while waiting for bids")? else { + return Ok(Summary::NoBids); + }; + + // TODO: report the pending nonce that we ended up using. + let transaction = Arc::unwrap_or_clone(winner) + .into_transaction_body( + self.pending_nonce.get(), + self.rollup_id, + &self.sequencer_key, + self.fee_asset_denomination.clone(), + self.sequencer_chain_id, + ) + .sign(self.sequencer_key.signing_key()); + + // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, + // it's likey lost. + // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once + // if the response didn't take too long? But it's probably a bad idea to even try. + // Can we detect if a submission failed due to a bad nonce? In this case, we could + // immediately ("optimistically") submit with the most recent pending nonce (if the + // publisher updated it in the meantime) or just nonce + 1 (if it didn't yet update)? + + let submission_fut = { + let client = self.sequencer_abci_client.clone(); + tokio::time::timeout(Duration::from_secs(30), async move { + client + .submit_transaction_sync(transaction) + .await + .wrap_err("submission request failed") + }) + }; + tokio::pin!(submission_fut); + + loop { + select!( + () = self.cancellation_token.clone().cancelled_owned(), if !self.cancellation_token.is_cancelled() => { + info!( + "received cancellation token while waiting for Sequencer to respond to \ + transaction submission; still waiting for submission until timeout" + ); + } + + res = &mut submission_fut => { + break match res + .wrap_err("submission of auction winner timed out before receiving a response from Sequencer") + { + Ok(Ok(rsp)) => Ok(Summary::Submitted(rsp)), + Err(err) | Ok(Err(err)) => Err(err), + } + } + ) + } + } + + async fn run_auction_loop(&mut self) -> eyre::Result>> { let mut latency_margin_timer = None; // TODO: do we want to make this configurable to allow for more complex allocation rules? let mut allocation_rule = FirstPrice::new(); let mut auction_is_open = false; - let auction_result = loop { + loop { select! { biased; _ = async { latency_margin_timer.as_mut().unwrap() }, if latency_margin_timer.is_some() => { info!("timer is up; bids left unprocessed: {}", self.bundles.len()); - break allocation_rule.winner(); + break Ok(allocation_rule.winner()); } Ok(()) = async { self.start_bids.as_mut().unwrap().await }, if self.start_bids.is_some() => { - let mut channel = self.start_bids.take().expect("inside an arm that that checks start_bids == Some"); + let mut channel = self + .start_bids + .take() + .expect("inside an arm that that checks start_bids == Some"); channel.close(); // TODO: if the timer is already running, report how much time is left for the bids auction_is_open = true; } Ok(()) = async { self.start_timer.as_mut().unwrap().await }, if self.start_timer.is_some() => { - let mut channel = self.start_timer.take().expect("inside an arm that checks start_immer == Some"); + let mut channel = self + .start_timer + .take() + .expect("inside an arm that checks start_timer == Some"); channel.close(); if !auction_is_open { - info!("received signal to start the auction timer before signal to process bids; that's ok but eats into the time allotment of the auction"); + info!( + "received signal to start the auction timer before signal to start \ + processing bids; that's ok but eats into the time allotment of the \ + auction" + ); } // TODO: Emit an event to report start and endpoint of the auction. @@ -135,52 +218,9 @@ impl Worker { } else => { - bail!("all channels are closed; this auction cannot continue") + bail!("all channels are closed; the auction cannot continue") } } - }; - - let Some(winner) = auction_result else { - info!("auction ended with no winning bid"); - return Ok(()); - }; - - // TODO: report the pending nonce that we ended up using. - let transaction = Arc::unwrap_or_clone(winner) - .into_transaction_body( - self.pending_nonce.get(), - self.rollup_id, - &self.sequencer_key, - self.fee_asset_denomination.clone(), - self.sequencer_chain_id, - ) - .sign(self.sequencer_key.signing_key()); - - // NOTE: Submit fire-and-forget style. If the submission didn't make it in time, - // it's likey lost. - // TODO: We can consider providing a very tight retry mechanism. Maybe resubmit once - // if the response didn't take too long? But it's probably a bad idea to even try. - // Can we detect if a submission failed due to a bad nonce? In this case, we could - // immediately ("optimistically") submit with the most recent pending nonce (if the - // publisher updated it in the meantime) or just nonce + 1 (if it didn't yet update)? - match self - .sequencer_abci_client - .submit_transaction_sync(transaction) - .await - .wrap_err("submission of the auction failed; it's likely lost") - { - Ok(resp) => { - // TODO: provide tx_sync response hash? Does it have extra meaning? - info!( - response.log = %resp.log, - response.code = resp.code.value(), - "auction winner submitted to sequencer", - ); - } - Err(error) => { - error!(%error, "failed to submit auction winner to sequencer; it's likely lost"); - } } - Ok(()) } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index 986dabfe81..d97af156d8 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -12,14 +12,14 @@ use astria_core::{ }; use astria_eyre::eyre::{ self, - bail, OptionExt as _, WrapErr as _, + bail, }; use futures::{ - stream::FuturesUnordered, Future, StreamExt as _, + stream::FuturesUnordered, }; use tokio::{ select, @@ -27,17 +27,19 @@ use tokio::{ }; use tokio_util::sync::CancellationToken; use tracing::{ + Level, + Span, error, field, info, info_span, instrument, warn, - Level, - Span, }; use crate::{ + Config, + Metrics, rollup_channel::{ BundleStream, ExecuteOptimisticBlockStream, @@ -48,8 +50,6 @@ use crate::{ SequencerChannel, }, sequencer_key::SequencerKey, - Config, - Metrics, }; mod auction; @@ -116,6 +116,7 @@ impl Inner { sequencer_chain_id, rollup_id, pending_nonce: pending_nonce.subscribe(), + cancellation_token: shutdown_token.child_token(), }; Ok(Self { @@ -198,26 +199,30 @@ impl Inner { /// Handles the result of an auction running to completion. /// - /// This method only exists to emit the auction result (only error right now) under a span. + /// This method only exists to ensure that panicking auctions receive an event. + /// It is assumed that auctions that ran to completion (returnin a success or failure) + /// will emit an event in their own span. #[instrument(skip_all, fields(%auction_id), err)] fn handle_completed_auction( &mut self, auction_id: auction::Id, - res: eyre::Result<()>, - ) -> eyre::Result<()> { + res: Result<(), tokio::task::JoinError>, + ) -> Result<(), tokio::task::JoinError> { self.running_auction.take(); res } /// Handles the result of cancelled auctions. /// - /// This method only exists to emit the auction result (only error right now) under a span. + /// This method only exists to ensure that panicking auctions receive an event. + /// It is assumed that auctions that ran to completion (returnin a success or failure) + /// will emit an event in their own span. #[instrument(skip_all, fields(%auction_id), err(level = Level::INFO))] fn handle_cancelled_auction( &self, auction_id: auction::Id, - res: eyre::Result<()>, - ) -> eyre::Result<()> { + res: Result<(), tokio::task::JoinError>, + ) -> Result<(), tokio::task::JoinError> { res } @@ -235,8 +240,8 @@ impl Inner { info!(auction_id = %new_auction.id(), "started new auction"); if let Some(old_auction) = self.running_auction.replace(new_auction) { - old_auction.abort(); - info!(auction_id = %old_auction.id(), "cancelled old auction"); + old_auction.cancel(); + info!(auction_id = %old_auction.id(), "cancelled running auction"); self.cancelled_auctions.push(old_auction); } @@ -378,8 +383,8 @@ impl PendingNoncePublisher { fn new(channel: SequencerChannel, address: Address) -> Self { use tokio::time::{ - timeout_at, MissedTickBehavior, + timeout_at, }; // TODO: make this configurable. Right now they assume a Sequencer block time of 2s, // so this is fetching nonce up to 4 times a block. From e17fe694c9c31881559792b9af9bc5e2e0582e3b Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 11 Dec 2024 21:05:51 +0100 Subject: [PATCH 72/73] some clippy changes to auctioneer --- crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs | 2 +- crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs | 2 +- crates/astria-auctioneer/src/auctioneer/inner/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index 3ca9275ab8..fe2754136d 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -119,7 +119,7 @@ impl Auction { } pub(super) fn cancel(&self) { - self.cancellation_token.cancel() + self.cancellation_token.cancel(); } pub(in crate::auctioneer::inner) fn id(&self) -> &Id { diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs index 891c619143..2b58b1830e 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/worker.rs @@ -165,7 +165,7 @@ impl Worker { Err(err) | Ok(Err(err)) => Err(err), } } - ) + ); } } diff --git a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs index d97af156d8..1871048904 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/mod.rs @@ -321,7 +321,7 @@ impl Inner { info!( auction_id = %running_auction.id(), "forwarded bundle to auction" - ) + ); } else { info!( "received a bundle but did not forward it to the auction because no auction was \ From 2edf112291f16038418a1ed96e45125a2cceec2d Mon Sep 17 00:00:00 2001 From: Richard Janis Goldschmidt Date: Wed, 11 Dec 2024 21:13:22 +0100 Subject: [PATCH 73/73] compare bundle parent hash against executed block hash, not parent of executed --- .../src/auctioneer/inner/auction/factory.rs | 2 +- .../src/auctioneer/inner/auction/mod.rs | 16 ++++++++-------- crates/astria-auctioneer/src/block/mod.rs | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs index 18a5465e79..c85e67ca8c 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/factory.rs @@ -67,7 +67,7 @@ impl Factory { id, block_hash, height, - parent_block_of_executed: None, + hash_of_executed_block_on_rollup: None, start_bids: Some(start_bids_tx), start_timer: Some(start_timer_tx), bundles: bundles_tx, diff --git a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs index fe2754136d..a06a10e2c8 100644 --- a/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs +++ b/crates/astria-auctioneer/src/auctioneer/inner/auction/mod.rs @@ -105,7 +105,7 @@ pub(super) struct Auction { id: Id, block_hash: BlockHash, height: u64, - parent_block_of_executed: Option<[u8; 32]>, + hash_of_executed_block_on_rollup: Option<[u8; 32]>, start_bids: Option>, start_timer: Option>, bundles: mpsc::UnboundedSender>, @@ -171,10 +171,10 @@ impl Auction { bail!("a previous executed block already triggered the auction to start bids"); } - let _prev_block = self - .parent_block_of_executed - .replace(block.parent_rollup_block_hash()); - debug_assert!(_prev_block.is_some()); + let prev_block = self + .hash_of_executed_block_on_rollup + .replace(block.rollup_block_hash()); + debug_assert!(prev_block.is_none()); Ok(()) } @@ -192,7 +192,7 @@ impl Auction { ) -> eyre::Result<()> { // TODO: emit some more information about auctoin ID, expected vs actual parent block hash, // tacked block hash, provided block hash, etc. - let Some(parent_block_of_executed) = self.parent_block_of_executed else { + let Some(block_hash_of_executed) = self.hash_of_executed_block_on_rollup else { eyre::bail!( "received a new bundle but the current auction has not yet received an execute block from the rollup; dropping the bundle" @@ -200,11 +200,11 @@ impl Auction { }; ensure!( &self.block_hash == bundle.base_sequencer_block_hash() - && parent_block_of_executed == bundle.parent_rollup_block_hash(), + && block_hash_of_executed == bundle.parent_rollup_block_hash(), "bundle does not match auction; auction.sequenecer_block_hash = `{}`, \ auction.parent_block_hash = `{}`, bundle. = `{}`, bundle.height = `{}`", self.block_hash, - base64(parent_block_of_executed), + base64(block_hash_of_executed), bundle.base_sequencer_block_hash(), base64(bundle.parent_rollup_block_hash()), ); diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index f736a4fdfa..850c6045d0 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -1,4 +1,5 @@ use astria_core::{ + Protobuf, execution, generated::astria::{ bundle::v1alpha1 as raw_bundle, @@ -9,19 +10,18 @@ use astria_core::{ }, primitive::v1::RollupId, sequencerblock::v1::{ + RollupTransactions, block::{ BlockHash, FilteredSequencerBlock, FilteredSequencerBlockParts, }, - RollupTransactions, }, - Protobuf, }; use astria_eyre::eyre::{ self, - eyre, Context, + eyre, }; use bytes::Bytes; use prost::Message as _; @@ -129,9 +129,9 @@ impl Executed { &self.sequencer_block_hash } - pub(crate) fn parent_rollup_block_hash(&self) -> [u8; 32] { + pub(crate) fn rollup_block_hash(&self) -> [u8; 32] { self.block - .parent_block_hash() + .hash() .as_ref() .try_into() .expect("rollup block hash must be 32 bytes")