Skip to content

Commit

Permalink
Reimplement block hash
Browse files Browse the repository at this point in the history
  • Loading branch information
lrubasze committed Nov 21, 2024
1 parent d37c370 commit 9e03a3b
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 35 deletions.
106 changes: 78 additions & 28 deletions core-rust/mesh-api-server/src/mesh_api/conversions/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,70 @@ use crate::prelude::*;

const MAX_API_STATE_VERSION: u64 = 100000000000000;

/// We assume that Block is a single transaction.
/// Block index => State version
/// Block hash => 32 bytes of: transaction_tree_hash[0..12] | receipt_tree_hash[0..12] | state_version
pub fn extract_state_version_from_block_hash(
database: &StateManagerDatabase<impl ReadableRocks>,
block_hash: &str,
) -> Result<StateVersion, ExtractionError> {
if block_hash.len() == 32 {
let hash_bytes =
hex::decode(block_hash).map_err(|_| ExtractionError::InvalidBlockIdentifier {
message: format!("Error decoding block hash {}", block_hash),
})?;

let mut index_bytes: [u8; 8] = [0; 8];
index_bytes.copy_from_slice(&hash_bytes[24..]);
let index = u64::from_be_bytes(index_bytes);

let state_version = StateVersion::of(index);
let transaction_identifiers = database
.get_committed_transaction_identifiers(state_version)
.ok_or_else(|| ExtractionError::NotFound)?;

let transaction_tree_hash = transaction_identifiers
.resultant_ledger_hashes
.transaction_root;
let receipt_tree_hash = transaction_identifiers.resultant_ledger_hashes.receipt_root;

if hash_bytes[..12] != transaction_tree_hash.as_slice()[0..12] {
return Err(ExtractionError::InvalidBlockIdentifier {
message: format!(
"Block hash {} does not match transaction tree hash",
block_hash
),
});
}
if hash_bytes[12..24] != receipt_tree_hash.as_slice()[0..12] {
return Err(ExtractionError::InvalidBlockIdentifier {
message: format!("Block hash {} does not match receipt tree hash", block_hash),
});
}

Ok(state_version)
} else {
Err(ExtractionError::InvalidBlockIdentifier {
message: format!("hash length {} not equal 32", block_hash.len()),
})
}
}

pub fn extract_state_version_from_mesh_api_partial_block_identifier(
database: &StateManagerDatabase<impl ReadableRocks>,
block_identifier: &models::PartialBlockIdentifier,
) -> Result<Option<StateVersion>, ExtractionError> {
let state_version = match (&block_identifier.hash, &block_identifier.index) {
(None, None) => None,
(Some(hash), None) => {
let index_from_hash =
hash.parse::<i64>()
.map_err(|_| ExtractionError::InvalidBlockIdentifier {
message: "Error converting hash to integer".to_string(),
})?;

Some(StateVersion::of(index_from_hash as u64))
}
(Some(hash), None) => Some(extract_state_version_from_block_hash(database, hash)?),
(None, Some(index)) => Some(StateVersion::of(*index as u64)),
(Some(hash), Some(index)) => {
let index_from_hash =
hash.parse::<i64>()
.map_err(|_| ExtractionError::InvalidBlockIdentifier {
message: "Error converting hash to integer".to_string(),
})?;
if *index == index_from_hash {
Some(StateVersion::of(index_from_hash as u64))
let state_version = extract_state_version_from_block_hash(database, hash)?;
if *index as u64 == state_version.number() {
Some(state_version)
} else {
return Err(ExtractionError::InvalidBlockIdentifier {
message: format!("Hash {} does not match index {}", index_from_hash, index),
message: format!("Hash {} does not match index {}", hash, index),
});
}
}
Expand All @@ -37,14 +75,12 @@ pub fn extract_state_version_from_mesh_api_partial_block_identifier(
}

pub fn extract_state_version_from_mesh_api_block_identifier(
database: &StateManagerDatabase<impl ReadableRocks>,
block_identifier: &models::BlockIdentifier,
) -> Result<StateVersion, ExtractionError> {
let index_from_hash = block_identifier.hash.parse::<i64>().map_err(|_| {
ExtractionError::InvalidBlockIdentifier {
message: "Error converting hash to integer".to_string(),
}
})?;
if block_identifier.index != index_from_hash {
let state_version_from_hash =
extract_state_version_from_block_hash(database, &block_identifier.hash)?;
if block_identifier.index as u64 != state_version_from_hash.number() {
Err(ExtractionError::InvalidBlockIdentifier {
message: format!(
"index {} and hash {} mismatch",
Expand All @@ -56,23 +92,37 @@ pub fn extract_state_version_from_mesh_api_block_identifier(
}
}

/// We assume that Block is a single transaction.
/// Block index => State version
/// Block hash => State version printed to string and prefixed with zeros
pub fn to_mesh_api_block_identifier_from_state_version(
database: &StateManagerDatabase<impl ReadableRocks>,
state_version: StateVersion,
) -> Result<models::BlockIdentifier, MappingError> {
let index = to_mesh_api_block_index_from_state_version(state_version)?;
let transaction_identifiers = database
.get_committed_transaction_identifiers(state_version)
.ok_or_else(|| MappingError::TransactionNotFound)?;

let transaction_tree_hash = transaction_identifiers
.resultant_ledger_hashes
.transaction_root;
let receipt_tree_hash = transaction_identifiers.resultant_ledger_hashes.receipt_root;

let mut hash_bytes = [0u8; 32];

hash_bytes[..12].copy_from_slice(&transaction_tree_hash.as_slice()[..12]);
hash_bytes[12..24].copy_from_slice(&receipt_tree_hash.as_slice()[..12]);
hash_bytes[24..].copy_from_slice((index as u64).to_be_bytes().as_slice());

Ok(models::BlockIdentifier {
index,
hash: format!("{:0>32}", index),
hash: hex::encode(hash_bytes),
})
}

pub fn to_mesh_api_block_identifier_from_ledger_header(
database: &StateManagerDatabase<impl ReadableRocks>,
ledger_header: &LedgerStateSummary,
) -> Result<models::BlockIdentifier, MappingError> {
to_mesh_api_block_identifier_from_state_version(ledger_header.state_version)
to_mesh_api_block_identifier_from_state_version(database, ledger_header.state_version)
}

pub fn to_mesh_api_block_index_from_state_version(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum MappingError {
InternalIndexDataMismatch {
message: String,
},
TransactionNotFound,
}

impl From<MappingError> for ResponseError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub(crate) async fn handle_account_balance(
// definitions
Ok(Json(models::AccountBalanceResponse {
block_identifier: Box::new(to_mesh_api_block_identifier_from_ledger_header(
database.deref(),
&header.into(),
)?),
balances,
Expand Down
12 changes: 8 additions & 4 deletions core-rust/mesh-api-server/src/mesh_api/handlers/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ pub(crate) async fn handle_block(
let database = state.state_manager.database.snapshot();
let mapping_context = MappingContext::new(&state.network);

let state_version =
extract_state_version_from_mesh_api_partial_block_identifier(&request.block_identifier)
.map_err(|err| err.into_response_error("block_identifier"))?
.unwrap_or_else(|| database.max_state_version());
let state_version = extract_state_version_from_mesh_api_partial_block_identifier(
database.deref(),
&request.block_identifier,
)
.map_err(|err| err.into_response_error("block_identifier"))?
.unwrap_or_else(|| database.max_state_version());

let previous_state_version = state_version.previous().map_err(|_| {
ResponseError::from(ApiError::ParentBlockNotAvailable).with_details(format!(
Expand Down Expand Up @@ -49,9 +51,11 @@ pub(crate) async fn handle_block(
// see https://docs.cdp.coinbase.com/mesh/docs/models#block
let block = models::Block {
block_identifier: Box::new(to_mesh_api_block_identifier_from_state_version(
database.deref(),
state_version,
)?),
parent_block_identifier: Box::new(to_mesh_api_block_identifier_from_state_version(
database.deref(),
previous_state_version,
)?),
timestamp: transaction_identifiers.proposer_timestamp_ms,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ pub(crate) async fn handle_block_transaction(
let database = state.state_manager.database.snapshot();
let mapping_context = MappingContext::new(&state.network);

let state_version =
extract_state_version_from_mesh_api_block_identifier(&request.block_identifier)
.map_err(|err| err.into_response_error("block_identifier"))?;
let state_version = extract_state_version_from_mesh_api_block_identifier(
database.deref(),
&request.block_identifier,
)
.map_err(|err| err.into_response_error("block_identifier"))?;

let transaction_identifiers = database
.get_committed_transaction_identifiers(state_version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) async fn handle_network_status(
.get_post_genesis_epoch_proof()
.map(|proof| -> Result<_, MappingError> {
Ok(Box::new(to_mesh_api_block_identifier_from_ledger_header(
database.deref(),
&proof.ledger_header.into(),
)?))
})
Expand All @@ -30,6 +31,7 @@ pub(crate) async fn handle_network_status(
.get_first_proof()
.map(|proof| -> Result<_, MappingError> {
Ok(Box::new(to_mesh_api_block_identifier_from_ledger_header(
database.deref(),
&proof.ledger_header.into(),
)?))
})
Expand All @@ -42,6 +44,7 @@ pub(crate) async fn handle_network_status(
.get_latest_proof()
.map(|proof| -> Result<_, MappingError> {
Ok(Box::new(to_mesh_api_block_identifier_from_ledger_header(
database.deref(),
&proof.ledger_header.into(),
)?))
})
Expand Down

0 comments on commit 9e03a3b

Please sign in to comment.