Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Return block authentication data with GetBlockHeaderByNum #345

Merged
merged 8 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 0.3.0 (TBD)

* Added block authentication data to the `GetBlockHeaderByNumber` RPC (#345).
* Added option to mint pulic notes in the faucet (#339).
* Renamed `note_hash` into `note_id` in the database (#336)
* Changed `version` and `timestamp` fields in `Block` message to `u32` (#337).
Expand Down
6 changes: 6 additions & 0 deletions crates/proto/proto/requests.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ message CheckNullifiersRequest {
repeated digest.Digest nullifiers = 1;
}

// Returns the block header corresponding to the requested block number, as well as the merkle
// path and current forest which validate the block's inclusion in the chain.
//
// The merkle path is an MMR proof for the block's leaf, based on the current forest.
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
message GetBlockHeaderByNumberRequest {
// The block number of the target block.
//
// If not provided, means latest know block.
optional uint32 block_num = 1;
// Whether or not to return authentication data for the block header.
bool include_authentication = 2;
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
}

// State synchronization request.
Expand Down
19 changes: 13 additions & 6 deletions crates/proto/proto/responses.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ message CheckNullifiersResponse {
}

message GetBlockHeaderByNumberResponse {
// The requested block header
block_header.BlockHeader block_header = 1;

// Merkle path to verify the block's inclusion in the MMR at the returned `forest`
optional merkle.MerklePath proof = 2;

// Current value of the MMR forest
optional fixed32 forest = 3;
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
}

message NullifierUpdate {
Expand All @@ -26,22 +33,22 @@ message NullifierUpdate {
}

message SyncStateResponse {
// number of the latest block in the chain
// Number of the latest block in the chain
fixed32 chain_tip = 1;

// block header of the block with the first note matching the specified criteria
// Block header of the block with the first note matching the specified criteria
block_header.BlockHeader block_header = 2;

// data needed to update the partial MMR from `block_num + 1` to `block_header.block_num`
// Data needed to update the partial MMR from `block_num + 1` to `block_header.block_num`
mmr.MmrDelta mmr_delta = 3;

// a list of account hashes updated after `block_num + 1` but not after `block_header.block_num`
// List of account hashes updated after `block_num + 1` but not after `block_header.block_num`
repeated account.AccountSummary accounts = 5;

// a list of all notes together with the Merkle paths from `block_header.note_root`
// List of all notes together with the Merkle paths from `block_header.note_root`
repeated note.NoteSyncRecord notes = 6;

// a list of nullifiers created between `block_num + 1` and `block_header.block_num`
// List of nullifiers created between `block_num + 1` and `block_header.block_num`
repeated NullifierUpdate nullifiers = 7;
}

Expand Down
7 changes: 7 additions & 0 deletions crates/proto/src/generated/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ pub struct CheckNullifiersRequest {
#[prost(message, repeated, tag = "1")]
pub nullifiers: ::prost::alloc::vec::Vec<super::digest::Digest>,
}
/// Returns the block header corresponding to the requested block number, as well as the merkle
/// path and current forest which validate the block's inclusion in the chain.
///
/// The merkle path is an MMR proof for the block's leaf, based on the current forest.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetBlockHeaderByNumberRequest {
Expand All @@ -19,6 +23,9 @@ pub struct GetBlockHeaderByNumberRequest {
/// If not provided, means latest know block.
#[prost(uint32, optional, tag = "1")]
pub block_num: ::core::option::Option<u32>,
/// Whether or not to return authentication data for the block header.
#[prost(bool, tag = "2")]
pub include_authentication: bool,
}
/// State synchronization request.
///
Expand Down
4 changes: 4 additions & 0 deletions crates/proto/src/generated/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub struct CheckNullifiersResponse {
pub struct GetBlockHeaderByNumberResponse {
#[prost(message, optional, tag = "1")]
pub block_header: ::core::option::Option<super::block_header::BlockHeader>,
#[prost(message, optional, tag = "2")]
pub proof: ::core::option::Option<super::merkle::MerklePath>,
#[prost(fixed32, optional, tag = "3")]
pub forest: ::core::option::Option<u32>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Gets a list of proofs for given nullifier hashes, each proof as a sparse Merkle

### GetBlockHeaderByNumber

Retrieves block header by given block number.
Retrieves block header by given block number, optionally alongside a Merkle path and the current MMR forest to validate its inclusion.
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

**Parameters**

Expand Down
2 changes: 1 addition & 1 deletion crates/store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Get a list of proofs for given nullifier hashes, each proof as a sparse Merkle T

### GetBlockHeaderByNumber

Retrieves block header by given block number.
Retrieves block header by given block number. Optionally, it also returns the MMR path and current forest to authenticate the block's inclusion.
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

**Parameters**

Expand Down
8 changes: 8 additions & 0 deletions crates/store/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ pub enum ApplyBlockError {
FailedToUpdateNullifierTree(NullifierTreeError),
}

#[derive(Error, Debug)]
pub enum GetBlockHeaderError {
#[error("Database error: {0}")]
DatabaseError(#[from] DatabaseError),
#[error("Error retrieving the merkle proof for the block: {0}")]
MmrError(#[from] MmrError),
}

#[derive(Error, Debug)]
pub enum GetBlockInputsError {
#[error("Database error: {0}")]
Expand Down
16 changes: 10 additions & 6 deletions crates/store/src/server/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,20 @@ impl api_server::Api for StoreApi {
request: tonic::Request<GetBlockHeaderByNumberRequest>,
) -> Result<Response<GetBlockHeaderByNumberResponse>, Status> {
info!(target: COMPONENT, ?request);
let request = request.into_inner();

let block_num = request.into_inner().block_num;
let block_header = self
let block_num = request.block_num;
let (block_header, merkle_proof) = self
.state
.get_block_header(block_num)
.get_block_header(block_num, request.include_authentication)
.await
.map_err(internal_error)?
.map(Into::into);
.map_err(internal_error)?;

Ok(Response::new(GetBlockHeaderByNumberResponse { block_header }))
Ok(Response::new(GetBlockHeaderByNumberResponse {
block_header: block_header.map(Into::into),
forest: merkle_proof.as_ref().map(|p| p.forest as u32),
proof: merkle_proof.map(|p| Into::into(p.merkle_path)),
}))
}

/// Returns info on whether the specified nullifiers have been consumed.
Expand Down
29 changes: 22 additions & 7 deletions crates/store/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use miden_objects::{
block::{Block, BlockNoteIndex, BlockNoteTree},
crypto::{
hash::rpo::RpoDigest,
merkle::{LeafIndex, Mmr, MmrDelta, MmrPeaks, SimpleSmt, SmtProof, ValuePath},
merkle::{LeafIndex, Mmr, MmrDelta, MmrPeaks, MmrProof, SimpleSmt, SmtProof, ValuePath},
},
notes::{NoteId, Nullifier},
transaction::OutputNote,
Expand All @@ -27,8 +27,8 @@ use crate::{
blocks::BlockStore,
db::{Db, NoteRecord, NullifierInfo, StateSyncUpdate},
errors::{
ApplyBlockError, DatabaseError, GetBlockInputsError, StateInitializationError,
StateSyncError,
ApplyBlockError, DatabaseError, GetBlockHeaderError, GetBlockInputsError,
StateInitializationError, StateSyncError,
},
nullifier_tree::NullifierTree,
types::{AccountId, BlockNumber},
Expand Down Expand Up @@ -291,15 +291,30 @@ impl State {
Ok(())
}

/// Queries a [BlockHeader] from the database.
/// Queries a [BlockHeader] from the database, and returns it alongside its inclusion proof.
///
/// If [None] is given as the value of `block_num`, the latest [BlockHeader] is returned.
/// If [None] is given as the value of `block_num`, the data for the latest [BlockHeader] is
/// returned.
#[instrument(target = "miden-store", skip_all, ret(level = "debug"), err)]
pub async fn get_block_header(
&self,
block_num: Option<BlockNumber>,
) -> Result<Option<BlockHeader>, DatabaseError> {
self.db.select_block_header_by_block_num(block_num).await
include_authentication: bool,
) -> Result<(Option<BlockHeader>, Option<MmrProof>), GetBlockHeaderError> {
let block_header = self.db.select_block_header_by_block_num(block_num).await?;
if let Some(header) = block_header {
let mmr_proof = if include_authentication {
let inner = self.inner.read().await;
let mmr_proof =
inner.chain_mmr.open(header.block_num() as usize, inner.chain_mmr.forest())?;
Some(mmr_proof)
} else {
None
};
Ok((Some(header), mmr_proof))
} else {
Ok((None, None))
}
}

/// Generates membership proofs for each one of the `nullifiers` against the latest nullifier
Expand Down