Skip to content
Draft
49 changes: 47 additions & 2 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::block_processing_utils::{
use crate::blocks_delay_tracker::BlocksDelayTracker;
use crate::chain_update::ChainUpdate;
use crate::crypto_hash_timer::CryptoHashTimer;
use crate::lightclient::get_epoch_block_producers_view;
use crate::lightclient::{
SpiceCertifiedBatchProof, get_epoch_block_producers_view, reconstruct_certified_lite_view,
};
use crate::missing_chunks::{MissingChunksPool, OptimisticBlockChunksPool};
use crate::orphan::{Orphan, OrphanBlockPool};
use crate::pending::PendingBlocksPool;
Expand Down Expand Up @@ -89,7 +91,7 @@ use near_primitives::version::{PROTOCOL_VERSION, ProtocolFeature};
use near_primitives::views::{
BlockStatusView, DroppedReason, ExecutionOutcomeWithIdView, ExecutionStatusView,
FinalExecutionOutcomeView, FinalExecutionOutcomeWithReceiptView, FinalExecutionStatus,
LightClientBlockView, SignedTransactionView,
LightClientBlockLiteView, LightClientBlockView, SignedTransactionView,
};
use near_store::adapter::StoreAdapter;
use near_store::adapter::chain_store::ChainStoreAdapter;
Expand Down Expand Up @@ -704,6 +706,48 @@ impl Chain {
create_light_client_block_view(&final_block_header, chain_store, Some(next_block_producers))
}

/// Spice: the certified-execution proof for `block_hash`, anchored to `head_block_hash`.
/// Two hops: `block_hash`'s leaf into its certifying block `C`'s batch root, then `C` into
/// `head`'s `block_merkle_root` (the existing consensus block proof).
pub fn spice_certified_batch_proof(
&self,
block_hash: &CryptoHash,
head_block_hash: &CryptoHash,
) -> Result<SpiceCertifiedBatchProof, Error> {
let certifying_block_hash = self.chain_store.get_certified_by_block(block_hash)?;
let certifying_block = self.chain_store.get_block(&certifying_block_hash)?;
let batch = self.spice_core_reader.certified_batch(
certifying_block.header().prev_hash(),
certifying_block.spice_core_statements(),
)?;
let batch_proof = batch.path_for(block_hash).ok_or_else(|| {
Error::Other(format!(
"block {block_hash} not certified by its indexed block {certifying_block_hash}"
))
})?;

let block_header = self.get_block_header(block_hash)?;
let (state_root, outcome_root) = self
.spice_core_reader
.certified_block_roots_for_certifying_block(&certifying_block, &block_header)?
.ok_or_else(|| Error::Other(format!("block {block_hash} is not certified")))?;
let block_header_lite =
reconstruct_certified_lite_view(&block_header, state_root, outcome_root);

let certifying_block_header_lite =
LightClientBlockLiteView::from(BlockHeader::clone(certifying_block.header()));
let block_proof = self.compute_past_block_proof_in_merkle_tree_of_later_block(
&certifying_block_hash,
head_block_hash,
)?;
Ok(SpiceCertifiedBatchProof {
block_header_lite,
batch_proof,
certifying_block_header_lite,
block_proof,
})
}

pub fn save_block(&mut self, block: MaybeValidated<Arc<Block>>) -> Result<(), Error> {
if self.chain_store.get_block(block.hash()).is_ok() {
return Ok(());
Expand Down Expand Up @@ -2569,6 +2613,7 @@ impl Chain {
self.spice_core_reader.validate_core_statements_in_block(&block).map_err(Box::new)?;
self.spice_core_reader.validate_prev_last_certified_block_epoch_id(header)?;
self.spice_core_reader.validate_spice_chunk_endorsement_stats(header)?;
self.spice_core_reader.validate_certified_batch_root(&block)?;
} else {
if block.is_spice_block() {
return Err(Error::Other(
Expand Down
11 changes: 11 additions & 0 deletions chain/chain/src/chain_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@ impl<'a> ChainUpdate<'a> {
self.epoch_manager.as_ref(),
&block,
)?;
// Index the blocks this block certifies onto itself, but only while it is canonical
// (the header head can be ahead from header sync); `update_height` re-points on reorg.
let is_canonical =
match self.chain_store_update.get_block_hash_by_height(block.header().height()) {
Ok(hash) => hash == *block.hash(),
Err(Error::DBNotFoundErr(_)) => false,
Err(err) => return Err(err),
};
if is_canonical {
self.chain_store_update.index_certified_by_block(&block)?;
}
}

// Update the chain head if it's the new tip
Expand Down
46 changes: 44 additions & 2 deletions chain/chain/src/lightclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@ use crate::ChainStoreAccess;
use near_chain_primitives::Error;
use near_epoch_manager::EpochManagerAdapter;
use near_primitives::block::BlockHeader;
use near_primitives::hash::hash;
use near_primitives::hash::{CryptoHash, hash};
use near_primitives::merkle::MerklePath;
use near_primitives::types::EpochId;
use near_primitives::views::validator_stake_view::ValidatorStakeView;
use near_primitives::views::{BlockHeaderInnerLiteView, LightClientBlockView};
use near_primitives::views::{
BlockHeaderInnerLiteView, LightClientBlockLiteView, LightClientBlockView,
};

/// Spice: the two-hop certified-execution proof for a block `H`. `H`'s leaf proves into the
/// batch root of its certifying block `C` (carried in `certifying_block_header_lite`), and
/// `C` proves into the trusted head's `block_merkle_root` via `block_proof`.
pub struct SpiceCertifiedBatchProof {
pub block_header_lite: LightClientBlockLiteView,
pub batch_proof: MerklePath,
pub certifying_block_header_lite: LightClientBlockLiteView,
pub block_proof: MerklePath,
}

pub fn get_epoch_block_producers_view(
epoch_id: &EpochId,
Expand All @@ -18,6 +31,33 @@ pub fn get_epoch_block_producers_view(
.collect::<Vec<_>>())
}

/// Light-client lite view of a certified spice block, with the certified roots in the
/// classic `prev_state_root` / `outcome_root` slots. Its `hash()` is the certified leaf.
pub fn reconstruct_certified_lite_view(
block_header: &BlockHeader,
certified_state_root: CryptoHash,
certified_outcome_root: CryptoHash,
) -> LightClientBlockLiteView {
let inner_lite = BlockHeaderInnerLiteView {
height: block_header.height(),
epoch_id: block_header.epoch_id().0,
next_epoch_id: block_header.next_epoch_id().0,
prev_state_root: certified_state_root,
outcome_root: certified_outcome_root,
timestamp: block_header.raw_timestamp(),
timestamp_nanosec: block_header.raw_timestamp(),
next_bp_hash: *block_header.next_bp_hash(),
block_merkle_root: *block_header.block_merkle_root(),
certified_block_merkle_root: block_header.certified_block_merkle_root().copied(),
last_certified_block: block_header.last_certified_block().copied(),
};
LightClientBlockLiteView {
prev_block_hash: *block_header.prev_hash(),
inner_rest_hash: hash(&block_header.inner_rest_bytes()),
inner_lite,
}
}

/// Creates the `LightClientBlock` from the information in the chain store for a given block.
///
/// # Arguments
Expand Down Expand Up @@ -46,6 +86,8 @@ pub fn create_light_client_block_view(
timestamp_nanosec: block_header.raw_timestamp(),
next_bp_hash: *block_header.next_bp_hash(),
block_merkle_root: *block_header.block_merkle_root(),
certified_block_merkle_root: block_header.certified_block_merkle_root().copied(),
last_certified_block: block_header.last_certified_block().copied(),
};
let inner_rest_hash = hash(&block_header.inner_rest_bytes());

Expand Down
Loading
Loading