Skip to content

Commit 6bf0fde

Browse files
Merge pull request #13395 from starkware-libs/dori/merge-main-v0.14.1-committer-into-main-v0.14.2-1774177856
Merge main-v0.14.1-committer into main-v0.14.2
2 parents 9729ba0 + 9096cff commit 6bf0fde

32 files changed

Lines changed: 782 additions & 135 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_consensus/src/manager.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,9 @@ impl<ContextT: ConsensusContext> MultiHeightManager<ContextT> {
445445
},
446446

447447
Err(err) => match err {
448-
e @ ConsensusError::BatcherError(_) | e @ ConsensusError::CommitteeError(_) => {
448+
e @ ConsensusError::BatcherError(_)
449+
| e @ ConsensusError::CommitteeError(_)
450+
| e @ ConsensusError::CendeAheadOfProposalHeight(_) => {
449451
error!(
450452
"Error while running consensus for height {height}, fallback to sync: {e}"
451453
);

crates/apollo_consensus/src/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ pub enum ConsensusError {
142142
BatcherError(#[from] BatcherClientError),
143143
#[error("Committee error: {0}")]
144144
CommitteeError(String),
145+
#[error("Cende recorder height >= proposal height ({0}); failing proposal")]
146+
CendeAheadOfProposalHeight(BlockNumber),
145147
// Indicates an error in communication between consensus and the node's networking component.
146148
// As opposed to an error between this node and peer nodes.
147149
#[error("{0}")]

crates/apollo_consensus_orchestrator/src/cende/mod.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use reqwest::Response;
3333
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, RequestBuilder};
3434
use reqwest_retry::policies::ExponentialBackoff;
3535
use reqwest_retry::{Jitter, RetryTransientMiddleware};
36-
use serde::Serialize;
36+
use serde::{Deserialize, Serialize};
3737
use shared_execution_objects::central_objects::CentralTransactionExecutionInfo;
3838
use starknet_api::block::{BlockHashAndNumber, BlockInfo, BlockNumber, StarknetVersion};
3939
use starknet_api::consensus_transaction::InternalConsensusTransaction;
@@ -100,6 +100,10 @@ pub trait CendeContext: Send + Sync {
100100
/// This function should return false if the previous height blob is not available.
101101
fn write_prev_height_blob(&self, current_height: BlockNumber) -> JoinHandle<bool>;
102102

103+
/// Returns the latest received block number from the recorder, or `None` if no blocks or on
104+
/// error.
105+
async fn get_latest_received_block(&self) -> Option<BlockNumber>;
106+
103107
// Prepares the previous height blob that will be written in the next height.
104108
async fn prepare_blob_for_next_height(
105109
&self,
@@ -113,13 +117,23 @@ pub struct CendeAmbassador {
113117
// `None` indicates that there is no blob to write, and therefore, the node can't be the
114118
// proposer.
115119
prev_height_blob: Arc<Mutex<Option<AerospikeBlob>>>,
116-
url: Url,
120+
write_blob_url: Url,
121+
get_latest_received_block_url: Url,
117122
client: ClientWithMiddleware,
118123
class_manager: SharedClassManagerClient,
119124
}
120125

121126
/// The path to write blob in the Recorder.
122127
pub const RECORDER_WRITE_BLOB_PATH: &str = "/cende_recorder/write_blob";
128+
/// The path to get the latest received block from the Recorder (the next block that will be written
129+
/// to DB. returns null when no blocks exist).
130+
pub const RECORDER_GET_LATEST_RECEIVED_BLOCK_PATH: &str =
131+
"/cende_recorder/get_latest_received_block";
132+
133+
#[derive(Debug, Deserialize)]
134+
struct GetLatestReceivedBlockResponse {
135+
block_number: Option<u64>,
136+
}
123137

124138
impl CendeAmbassador {
125139
pub fn new(cende_config: CendeConfig, class_manager: SharedClassManagerClient) -> Self {
@@ -130,12 +144,14 @@ impl CendeAmbassador {
130144

131145
CendeAmbassador {
132146
prev_height_blob: Arc::new(Mutex::new(None)),
133-
url: {
134-
let mut recorder_url = cende_config.recorder_url;
135-
recorder_url =
136-
recorder_url.join(RECORDER_WRITE_BLOB_PATH).expect("Failed to construct URL");
137-
recorder_url
138-
},
147+
write_blob_url: cende_config
148+
.recorder_url
149+
.join(RECORDER_WRITE_BLOB_PATH)
150+
.expect("Failed to construct write blob URL"),
151+
get_latest_received_block_url: cende_config
152+
.recorder_url
153+
.join(RECORDER_GET_LATEST_RECEIVED_BLOCK_PATH)
154+
.expect("Failed to construct get latest received block URL"),
139155
client: ClientBuilder::new(reqwest::Client::new())
140156
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
141157
.build(),
@@ -150,7 +166,7 @@ impl CendeContext for CendeAmbassador {
150166
info!("Start writing to Aerospike previous height blob for height {current_height}.");
151167

152168
let prev_height_blob = self.prev_height_blob.clone();
153-
let request_builder = self.client.post(self.url.clone());
169+
let request_builder = self.client.post(self.write_blob_url.clone());
154170

155171
task::spawn(
156172
async move {
@@ -189,6 +205,33 @@ impl CendeContext for CendeAmbassador {
189205
)
190206
}
191207

208+
async fn get_latest_received_block(&self) -> Option<BlockNumber> {
209+
match self.client.get(self.get_latest_received_block_url.clone()).send().await {
210+
Ok(response) if response.status().is_success() => {
211+
match response.json::<GetLatestReceivedBlockResponse>().await {
212+
Ok(resp) => resp.block_number.map(BlockNumber),
213+
Err(e) => {
214+
warn!("Failed to parse recorder get_latest_received_block response: {e}");
215+
None
216+
}
217+
}
218+
}
219+
// Response is not successful: log and return None.
220+
Ok(response) => {
221+
warn!(
222+
"Recorder get_latest_received_block returned error status {}: {}",
223+
response.status(),
224+
response.text().await.unwrap_or_else(|_| "unparseable".to_string())
225+
);
226+
None
227+
}
228+
Err(e) => {
229+
warn!("Failed to request recorder get_latest_received_block: {e}");
230+
None
231+
}
232+
}
233+
}
234+
192235
#[sequencer_latency_histogram(CENDE_PREPARE_BLOB_FOR_NEXT_HEIGHT_LATENCY, false)]
193236
async fn prepare_blob_for_next_height(
194237
&self,

crates/apollo_consensus_orchestrator/src/sequencer_consensus_context.rs

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ type ProposalContent =
114114
type HeightToRoundToProposal =
115115
BTreeMap<BlockNumber, BTreeMap<Round, (ProposalCommitment, ProposalContent)>>;
116116

117+
/// Cende status relative to the block we're building.
118+
#[derive(Debug)]
119+
pub(crate) enum CendeStatus {
120+
/// Cende has current block (or higher) written; fail the proposal round.
121+
CurrentBlob,
122+
/// Cende has prev block written; skip writing, proceed with build.
123+
PrevBlob,
124+
/// Prev blob not yet written; must write before building.
125+
PrevBlobMissing,
126+
}
127+
117128
pub(crate) struct BuiltProposals {
118129
// {height: {proposal_commitment: (init, content, proposal_id, finished_info)}}
119130
// Note that multiple proposals IDs can be associated with the same content, but we only need
@@ -289,26 +300,21 @@ impl SequencerConsensusContext {
289300
StreamSender { proposal_sender }
290301
}
291302

292-
async fn get_latest_sync_height(&self) -> Option<BlockNumber> {
293-
match self.deps.state_sync_client.get_latest_block_number().await {
294-
Ok(height) => height,
295-
Err(e) => {
296-
error!("Failed to get latest sync height: {e:?}");
297-
None
298-
}
299-
}
300-
}
301-
302-
async fn can_skip_write_prev_height_blob(&self, height: BlockNumber) -> bool {
303+
async fn get_cende_status(&self, height: BlockNumber) -> CendeStatus {
303304
if height == BlockNumber(0) {
304-
return true;
305+
return CendeStatus::PrevBlob;
305306
}
306-
match self.get_latest_sync_height().await {
307-
Some(latest_sync_height) => {
308-
latest_sync_height
309-
>= height.prev().expect("Height should be greater than 0. Checked above.")
307+
match self.deps.cende_ambassador.get_latest_received_block().await {
308+
Some(latest_cende_block) => {
309+
if latest_cende_block >= height {
310+
CendeStatus::CurrentBlob
311+
} else if latest_cende_block >= height.prev().expect("height > 0 has predecessor") {
312+
CendeStatus::PrevBlob
313+
} else {
314+
CendeStatus::PrevBlobMissing
315+
}
310316
}
311-
None => false,
317+
None => CendeStatus::PrevBlobMissing,
312318
}
313319
}
314320

@@ -535,18 +541,20 @@ impl ConsensusContext for SequencerConsensusContext {
535541
build_param: BuildParam,
536542
timeout: Duration,
537543
) -> Result<oneshot::Receiver<ProposalCommitment>, ConsensusError> {
538-
let cende_write_success = if self.can_skip_write_prev_height_blob(build_param.height).await
539-
{
540-
// cende_write_success is a AbortOnDropHandle. To get the actual handle we need to
541-
// spawn the task.
542-
AbortOnDropHandle::new(tokio::spawn(ready(true)))
543-
} else {
544-
// TODO(dvir): consider start writing the blob in `decision_reached`, to reduce
545-
// transactions finality time. Use this option only for one special
546-
// sequencer that is the same cluster as the recorder.
547-
AbortOnDropHandle::new(
548-
self.deps.cende_ambassador.write_prev_height_blob(build_param.height),
549-
)
544+
let status = self.get_cende_status(build_param.height).await;
545+
let cende_write_success = match status {
546+
CendeStatus::CurrentBlob => {
547+
return Err(ConsensusError::CendeAheadOfProposalHeight(build_param.height));
548+
}
549+
CendeStatus::PrevBlob => AbortOnDropHandle::new(tokio::spawn(ready(true))),
550+
CendeStatus::PrevBlobMissing => {
551+
// TODO(dvir): consider start writing the blob in `decision_reached`, to reduce
552+
// transactions finality time. Use this option only for one special
553+
// sequencer that is the same cluster as the recorder.
554+
AbortOnDropHandle::new(
555+
self.deps.cende_ambassador.write_prev_height_blob(build_param.height),
556+
)
557+
}
550558
};
551559

552560
// Handles interrupting an active proposal from a previous height/round

0 commit comments

Comments
 (0)