Skip to content

Commit 26f0662

Browse files
authored
Key the execution state cache with the block hash (#4842)
## Motivation If a confirmed certificate was submitted to a validator with a correct execution state hash, but with an incorrect execution outcome, if the validator already had the execution state hash in its cache, it wouldn't re-execute the certificate and notice anything wrong. ## Proposal Use the block hash instead of the execution state hash as the key in `execution_state_cache`. This way if the outcomes are wrong, the validator will re-execute the block and notice mismatching outcomes. ## Test Plan CI ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist) - #4824 - a related PR on the `testnet_conway` branch that exposed the issue.
1 parent f8b7185 commit 26f0662

File tree

2 files changed

+37
-36
lines changed

2 files changed

+37
-36
lines changed

linera-core/src/chain_worker/state.rs

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ where
723723
notify_when_messages_are_delivered: Option<oneshot::Sender<()>>,
724724
) -> Result<(ChainInfoResponse, NetworkActions, BlockOutcome), WorkerError> {
725725
let block = certificate.block();
726+
let block_hash = certificate.hash();
726727
let height = block.header.height;
727728
let chain_id = block.header.chain_id;
728729

@@ -871,29 +872,28 @@ where
871872
.await?;
872873
let oracle_responses = Some(block.body.oracle_responses.clone());
873874
let (proposed_block, outcome) = block.clone().into_proposal();
874-
let verified_outcome = if let Some(mut execution_state) =
875-
self.execution_state_cache.remove(&outcome.state_hash)
876-
{
877-
chain.execution_state = execution_state
878-
.with_context(|ctx| {
879-
chain
880-
.execution_state
881-
.context()
882-
.clone_with_base_key(ctx.base_key().bytes.clone())
883-
})
884-
.await;
885-
outcome.clone()
886-
} else {
887-
chain
888-
.execute_block(
889-
&proposed_block,
890-
local_time,
891-
None,
892-
&published_blobs,
893-
oracle_responses,
894-
)
895-
.await?
896-
};
875+
let verified_outcome =
876+
if let Some(mut execution_state) = self.execution_state_cache.remove(&block_hash) {
877+
chain.execution_state = execution_state
878+
.with_context(|ctx| {
879+
chain
880+
.execution_state
881+
.context()
882+
.clone_with_base_key(ctx.base_key().bytes.clone())
883+
})
884+
.await;
885+
outcome.clone()
886+
} else {
887+
chain
888+
.execute_block(
889+
&proposed_block,
890+
local_time,
891+
None,
892+
&published_blobs,
893+
oracle_responses,
894+
)
895+
.await?
896+
};
897897
// We should always agree on the messages and state hash.
898898
ensure!(
899899
outcome == verified_outcome,
@@ -1301,7 +1301,7 @@ where
13011301
let (_, committee) = self.chain.current_committee()?;
13021302
block.check_proposal_size(committee.policy().maximum_block_proposal_size)?;
13031303

1304-
let outcome = self
1304+
let executed_block = self
13051305
.execute_block(&block, local_time, round, published_blobs)
13061306
.await?;
13071307

@@ -1317,7 +1317,7 @@ where
13171317
.await?;
13181318
}
13191319

1320-
Ok((outcome.with(block), response))
1320+
Ok((executed_block, response))
13211321
}
13221322

13231323
/// Validates and executes a block proposed to extend this chain.
@@ -1426,28 +1426,27 @@ where
14261426
self.chain
14271427
.remove_bundles_from_inboxes(block.timestamp, true, block.incoming_bundles())
14281428
.await?;
1429-
let outcome = if let Some(outcome) = outcome {
1430-
outcome.clone()
1429+
let block = if let Some(outcome) = outcome {
1430+
outcome.clone().with(proposal.content.block.clone())
14311431
} else {
14321432
self.execute_block(block, local_time, round.multi_leader(), &published_blobs)
14331433
.await?
14341434
};
14351435

14361436
ensure!(
1437-
!round.is_fast() || !outcome.has_oracle_responses(),
1437+
!round.is_fast() || !block.has_oracle_responses(),
14381438
WorkerError::FastBlockUsingOracles
14391439
);
14401440
let chain = &mut self.chain;
14411441
// Check if the counters of tip_state would be valid.
14421442
chain
14431443
.tip_state
14441444
.get_mut()
1445-
.update_counters(&block.transactions, &outcome.messages)?;
1445+
.update_counters(&block.body.transactions, &block.body.messages)?;
14461446
// Don't save the changes since the block is not confirmed yet.
14471447
chain.rollback();
14481448

14491449
// Create the vote and store it in the chain state.
1450-
let block = outcome.with(proposal.content.block.clone());
14511450
let created_blobs: BTreeMap<_, _> = block.iter_created_blobs().collect();
14521451
let blobs = self
14531452
.get_required_blobs(proposal.expected_blob_ids(), &created_blobs)
@@ -1553,21 +1552,23 @@ where
15531552
local_time: Timestamp,
15541553
round: Option<u32>,
15551554
published_blobs: &[Blob],
1556-
) -> Result<BlockExecutionOutcome, WorkerError> {
1555+
) -> Result<Block, WorkerError> {
15571556
let outcome =
15581557
Box::pin(
15591558
self.chain
15601559
.execute_block(block, local_time, round, published_blobs, None),
15611560
)
15621561
.await?;
1562+
let block = Block::new(block.clone(), outcome);
1563+
let block_hash = CryptoHash::new(&block);
15631564
self.execution_state_cache.insert_owned(
1564-
&outcome.state_hash,
1565+
&block_hash,
15651566
self.chain
15661567
.execution_state
15671568
.with_context(|ctx| InactiveContext(ctx.base_key().clone()))
15681569
.await,
15691570
);
1570-
Ok(outcome)
1571+
Ok(block)
15711572
}
15721573

15731574
/// Initializes and saves the current chain if it is not active yet.

linera-core/src/unit_tests/worker_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ where
708708
let value = ConfirmedBlock::new(
709709
BlockExecutionOutcome {
710710
state_hash,
711-
messages: vec![vec![]],
711+
messages: vec![vec![direct_credit_message(chain_2, small_transfer)]],
712712
oracle_responses: vec![vec![]],
713713
events: vec![vec![]],
714714
blobs: vec![vec![]],
@@ -4185,14 +4185,14 @@ where
41854185

41864186
let value = ConfirmedBlock::new(
41874187
BlockExecutionOutcome {
4188-
messages: vec![vec![]],
4188+
messages: vec![vec![direct_credit_message(chain_2, small_transfer)]],
41894189
previous_message_blocks: BTreeMap::new(),
41904190
previous_event_blocks: BTreeMap::new(),
41914191
events: vec![vec![]],
41924192
blobs: vec![vec![]],
41934193
state_hash: state.crypto_hash_mut().await?,
41944194
oracle_responses: vec![vec![]],
4195-
operation_results: vec![],
4195+
operation_results: vec![OperationResult::default()],
41964196
}
41974197
.with(block),
41984198
);

0 commit comments

Comments
 (0)