mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-06-29 18:39:30 +00:00
feat(common): add block hash recomputation helper
test: `RISC0_DEV_MODE=1 RISC0_SKIP_BUILD=1 cargo test -p common --lib block::tests`
This commit is contained in:
parent
b728074225
commit
f0dec9dd72
@ -55,6 +55,21 @@ pub struct Block {
|
||||
pub bedrock_status: BedrockStatus,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Recomputes the hash from this block's contents, for integrity verification
|
||||
/// against the value stored in `header.hash`.
|
||||
#[must_use]
|
||||
pub fn recompute_hash(&self) -> BlockHash {
|
||||
HashableBlockData {
|
||||
block_id: self.header.block_id,
|
||||
prev_block_hash: self.header.prev_block_hash,
|
||||
timestamp: self.header.timestamp,
|
||||
transactions: self.body.transactions.clone(),
|
||||
}
|
||||
.compute_hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Block {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
crate::borsh_base64::serialize(self, serializer)
|
||||
@ -76,11 +91,13 @@ pub struct HashableBlockData {
|
||||
}
|
||||
|
||||
impl HashableBlockData {
|
||||
/// Domain-separated hash of the block contents: `SHA256(PREFIX || borsh(self))`.
|
||||
/// The single source of truth for both producing and verifying a block hash.
|
||||
#[must_use]
|
||||
pub fn into_pending_block(self, signing_key: &lee::PrivateKey) -> Block {
|
||||
pub fn compute_hash(&self) -> BlockHash {
|
||||
const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Block/\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
let data_bytes = borsh::to_vec(&self).unwrap();
|
||||
let data_bytes = borsh::to_vec(self).unwrap();
|
||||
let mut bytes = Vec::with_capacity(
|
||||
PREFIX
|
||||
.len()
|
||||
@ -89,8 +106,12 @@ impl HashableBlockData {
|
||||
);
|
||||
bytes.extend_from_slice(PREFIX);
|
||||
bytes.extend_from_slice(&data_bytes);
|
||||
OwnHasher::hash(&bytes)
|
||||
}
|
||||
|
||||
let hash = OwnHasher::hash(&bytes);
|
||||
#[must_use]
|
||||
pub fn into_pending_block(self, signing_key: &lee::PrivateKey) -> Block {
|
||||
let hash = self.compute_hash();
|
||||
let signature = lee::Signature::new(signing_key, &hash.0);
|
||||
Block {
|
||||
header: BlockHeader {
|
||||
@ -132,4 +153,33 @@ mod tests {
|
||||
let block_from_bytes = borsh::from_slice::<HashableBlockData>(&bytes).unwrap();
|
||||
assert_eq!(hashable, block_from_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recompute_hash_matches_header_for_well_formed_block() {
|
||||
let key = lee::PrivateKey::try_new([7_u8; 32]).expect("valid key");
|
||||
let block = HashableBlockData {
|
||||
block_id: 5,
|
||||
prev_block_hash: HashType([9_u8; 32]),
|
||||
timestamp: 42,
|
||||
transactions: vec![test_utils::produce_dummy_empty_transaction()],
|
||||
}
|
||||
.into_pending_block(&key);
|
||||
assert_eq!(block.recompute_hash(), block.header.hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recompute_hash_detects_tampering() {
|
||||
let key = lee::PrivateKey::try_new([7_u8; 32]).expect("valid key");
|
||||
let block = HashableBlockData {
|
||||
block_id: 5,
|
||||
prev_block_hash: HashType([9_u8; 32]),
|
||||
timestamp: 42,
|
||||
transactions: vec![test_utils::produce_dummy_empty_transaction()],
|
||||
}
|
||||
.into_pending_block(&key);
|
||||
|
||||
let mut tampered = block.clone();
|
||||
tampered.header.timestamp = 99; // header changed; stale hash no longer matches
|
||||
assert_ne!(tampered.recompute_hash(), tampered.header.hash);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user