Skip to content
Merged
Changes from all 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
190 changes: 189 additions & 1 deletion crates/apollo_committer/src/request_paths_and_commit_block_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ use apollo_committer_types::committer_types::{
use indexmap::indexmap;
use starknet_api::block::BlockNumber;
use starknet_api::block_hash::state_diff_hash::calculate_state_diff_hash;
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, StateDiffCommitment};
use starknet_api::core::{
ClassHash,
CompiledClassHash,
ContractAddress,
StateDiffCommitment,
PATRICIA_KEY_UPPER_BOUND_FELT,
};
use starknet_api::hash::HashOutput;
use starknet_api::state::ThinStateDiff;
use starknet_committer::block_committer::input::{
Expand All @@ -30,6 +36,7 @@ use starknet_committer::patricia_merkle_tree::types::{
CompiledClassHash as CommitterCompiledClassHash,
StarknetForestProofs,
};
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{BinaryData, NodeData};
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
use starknet_patricia::patricia_merkle_tree::storage_proof_verification::verify_patricia_proof;
use starknet_patricia::patricia_merkle_tree::types::NodeIndex;
Expand Down Expand Up @@ -359,3 +366,184 @@ async fn revert_removes_witnesses_and_digest() {
assert_witnesses_and_digest_absent(&mut committer, BlockNumber(height_1)).await;
assert_eq!(committer.offset, BlockNumber(height_1));
}

/// Flow overview:
/// 1. Commit block 0 with three class leaves that form this Patricia topology:
/// ```text
/// R
/// / \
/// E F
/// | |
/// G |
/// / \ \
/// A B D
/// ```
/// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
/// and requesting witnesses only for the deleted key.
/// 3. Assert the returned classes-trie proof contains node `G` (not strictly necessary, see comment
/// below).
#[tokio::test]
async fn test_bottom_of_new_edge_to_an_unmoidifed_subtree_is_present() {
// Set the two leftmost and the rightmost leaves.
let class_hash_a = ClassHash(0_u64.into());
let class_hash_b = ClassHash(1_u64.into());
let class_hash_d = ClassHash(PATRICIA_KEY_UPPER_BOUND_FELT - 1_u64);

let compiled_class_hash_a_felt = 100_u64.into();
let compiled_class_hash_b_felt = 101_u64.into();
let compiled_class_hash_d_felt = 102_u64.into();

let mut committer = new_test_committer().await;
let height_0 = 0;
let height_1 = 1;
let block_0_state_diff = ThinStateDiff {
class_hash_to_compiled_class_hash: indexmap! {
class_hash_a => CompiledClassHash(compiled_class_hash_a_felt),
class_hash_b => CompiledClassHash(compiled_class_hash_b_felt),
class_hash_d => CompiledClassHash(compiled_class_hash_d_felt),
},
..Default::default()
};
let block_1_state_diff = ThinStateDiff {
class_hash_to_compiled_class_hash: indexmap! {
class_hash_d => CompiledClassHash(0_u64.into()),
},
..Default::default()
};
let accessed_keys = AccessedKeys {
accessed_class_hashes: BTreeSet::from([class_hash_d]),
..Default::default()
};

committer
.commit_block(CommitBlockRequest {
state_diff: block_0_state_diff.clone(),
state_diff_commitment: Some(calculate_state_diff_hash(&block_0_state_diff)),
height: BlockNumber(height_0),
})
.await
.unwrap();

let response = committer
.read_paths_and_commit_block(read_paths_and_commit_block_request(
block_1_state_diff.clone(),
Some(calculate_state_diff_hash(&block_1_state_diff)),
height_1,
accessed_keys,
))
.await
.unwrap();

let leaf_a_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
compiled_class_hash_a_felt,
));
let leaf_b_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
compiled_class_hash_b_felt,
));
let node_g_hash = TreeHashFunctionImpl::compute_node_hash(&NodeData::<
CommitterCompiledClassHash,
HashOutput,
>::Binary(BinaryData {
left_data: leaf_a_hash,
right_data: leaf_b_hash,
}));

// TODO(Ariel): the preimage of G is not really needed by the OS (it only needs R, F, and the
// new root R', whose opening contains the hash of G). Change this to not contains or delete
// this test after making request_paths_and_commit_block_request stricter.
//
// For completeness, the OS verifies:
// hash(G_hash, truncated_path) + (len([R',G]) - 1) == E_hash.
// This in turn also proves that G is not an edge node, as it's the bottom of an old
// edge node, without explicitly requesting an opening of E.
assert!(
response.patricia_proofs.classes_trie_proof.contains_key(&node_g_hash),
"missing bottom of a new edge node in a proof",
);
}

/// Flow overview:
/// 1. Commit block 0 with three class leaves that form this Patricia topology:
/// ```text
/// R
/// |
/// T
/// / \
/// E F
/// / \ \
/// A B D
/// ```
/// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
/// and requesting witnesses only for the deleted key.
/// 3. Assert the returned classes-trie proof contains node `E` (this will allow verifying that the
/// new edge's bottom is not an edge).
#[tokio::test]
async fn test_bottom_of_new_edge_which_was_not_bottom_of_an_old_edge_is_present() {
let class_hash_a = ClassHash(0_u64.into());
let class_hash_b = ClassHash(1_u64.into());
let class_hash_d = ClassHash(3_u64.into());

let compiled_class_hash_a_felt = 100_u64.into();
let compiled_class_hash_b_felt = 101_u64.into();
let compiled_class_hash_d_felt = 102_u64.into();

let mut committer = new_test_committer().await;
let height_0 = 0;
let height_1 = 1;
let block_0_state_diff = ThinStateDiff {
class_hash_to_compiled_class_hash: indexmap! {
class_hash_a => CompiledClassHash(compiled_class_hash_a_felt),
class_hash_b => CompiledClassHash(compiled_class_hash_b_felt),
class_hash_d => CompiledClassHash(compiled_class_hash_d_felt),
},
..Default::default()
};
let block_1_state_diff = ThinStateDiff {
class_hash_to_compiled_class_hash: indexmap! {
class_hash_d => CompiledClassHash(0_u64.into()),
},
..Default::default()
};
let accessed_keys = AccessedKeys {
accessed_class_hashes: BTreeSet::from([class_hash_d]),
..Default::default()
};

committer
.commit_block(CommitBlockRequest {
state_diff: block_0_state_diff.clone(),
state_diff_commitment: Some(calculate_state_diff_hash(&block_0_state_diff)),
height: BlockNumber(height_0),
})
.await
.unwrap();

let response = committer
.read_paths_and_commit_block(read_paths_and_commit_block_request(
block_1_state_diff.clone(),
Some(calculate_state_diff_hash(&block_1_state_diff)),
height_1,
accessed_keys,
))
.await
.unwrap();

let leaf_a_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
compiled_class_hash_a_felt,
));
let leaf_b_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
compiled_class_hash_b_felt,
));
let node_e_hash = TreeHashFunctionImpl::compute_node_hash(&NodeData::<
CommitterCompiledClassHash,
HashOutput,
>::Binary(BinaryData {
left_data: leaf_a_hash,
right_data: leaf_b_hash,
}));

assert!(
response.patricia_proofs.classes_trie_proof.contains_key(&node_e_hash),
"missing bottom of a new edge node in a proof",
);
Comment thread
ArielElp marked this conversation as resolved.
}
Loading