Skip to content

Commit 11ad4ad

Browse files
committed
apollo_committer: test new edge bottom in witnesses
1 parent eae40db commit 11ad4ad

1 file changed

Lines changed: 184 additions & 1 deletion

File tree

crates/apollo_committer/src/request_paths_and_commit_block_tests.rs

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ use apollo_committer_types::committer_types::{
1010
use indexmap::indexmap;
1111
use starknet_api::block::BlockNumber;
1212
use starknet_api::block_hash::state_diff_hash::calculate_state_diff_hash;
13-
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, StateDiffCommitment};
13+
use starknet_api::core::{
14+
ClassHash,
15+
CompiledClassHash,
16+
ContractAddress,
17+
StateDiffCommitment,
18+
PATRICIA_KEY_UPPER_BOUND_FELT,
19+
};
1420
use starknet_api::hash::HashOutput;
1521
use starknet_api::state::ThinStateDiff;
1622
use starknet_committer::block_committer::input::{
@@ -30,6 +36,7 @@ use starknet_committer::patricia_merkle_tree::types::{
3036
CompiledClassHash as CommitterCompiledClassHash,
3137
StarknetForestProofs,
3238
};
39+
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{BinaryData, NodeData};
3340
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
3441
use starknet_patricia::patricia_merkle_tree::storage_proof_verification::verify_patricia_proof;
3542
use starknet_patricia::patricia_merkle_tree::types::NodeIndex;
@@ -359,3 +366,179 @@ async fn revert_removes_witnesses_and_digest() {
359366
assert_witnesses_and_digest_absent(&mut committer, BlockNumber(height_1)).await;
360367
assert_eq!(committer.offset, BlockNumber(height_1));
361368
}
369+
370+
/// Flow overview:
371+
/// 1. Commit block 0 with three class leaves that form this Patricia topology:
372+
/// ```text
373+
/// R
374+
/// / \
375+
/// E F
376+
/// | |
377+
/// G |
378+
/// / \ \
379+
/// A B D
380+
/// ```
381+
/// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
382+
/// and requesting witnesses only for the deleted key.
383+
/// 3. Assert the returned classes-trie proof contains node `G` (not strictly necessary, see comment
384+
/// below).
385+
#[tokio::test]
386+
async fn test_bottom_of_new_edge_to_an_unmoidifed_subtree_is_present() {
387+
// Set the two leftmost and the rightmost leaves.
388+
let class_hash_a = ClassHash(0_u64.into());
389+
let class_hash_b = ClassHash(1_u64.into());
390+
let class_hash_d = ClassHash(PATRICIA_KEY_UPPER_BOUND_FELT - 1_u64);
391+
392+
let compiled_class_hash_a_felt = 100_u64.into();
393+
let compiled_class_hash_b_felt = 101_u64.into();
394+
let compiled_class_hash_d_felt = 102_u64.into();
395+
396+
let mut committer = new_test_committer().await;
397+
let height_0 = 0;
398+
let height_1 = 1;
399+
let block_0_state_diff = ThinStateDiff {
400+
class_hash_to_compiled_class_hash: indexmap! {
401+
class_hash_a => CompiledClassHash(compiled_class_hash_a_felt),
402+
class_hash_b => CompiledClassHash(compiled_class_hash_b_felt),
403+
class_hash_d => CompiledClassHash(compiled_class_hash_d_felt),
404+
},
405+
..Default::default()
406+
};
407+
let block_1_state_diff = ThinStateDiff {
408+
class_hash_to_compiled_class_hash: indexmap! {
409+
class_hash_d => CompiledClassHash(0_u64.into()),
410+
},
411+
..Default::default()
412+
};
413+
let accessed_keys = AccessedKeys {
414+
accessed_class_hashes: BTreeSet::from([class_hash_d]),
415+
..Default::default()
416+
};
417+
418+
committer
419+
.commit_block(CommitBlockRequest {
420+
state_diff: block_0_state_diff.clone(),
421+
state_diff_commitment: Some(calculate_state_diff_hash(&block_0_state_diff)),
422+
height: BlockNumber(height_0),
423+
})
424+
.await
425+
.unwrap();
426+
427+
let response = committer
428+
.read_paths_and_commit_block(read_paths_and_commit_block_request(
429+
block_1_state_diff.clone(),
430+
Some(calculate_state_diff_hash(&block_1_state_diff)),
431+
height_1,
432+
accessed_keys,
433+
))
434+
.await
435+
.unwrap();
436+
437+
let leaf_a_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
438+
compiled_class_hash_a_felt,
439+
));
440+
let leaf_b_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
441+
compiled_class_hash_b_felt,
442+
));
443+
let node_g_hash = TreeHashFunctionImpl::compute_node_hash(&NodeData::<
444+
CommitterCompiledClassHash,
445+
HashOutput,
446+
>::Binary(BinaryData {
447+
left_data: leaf_a_hash,
448+
right_data: leaf_b_hash,
449+
}));
450+
451+
// TODO(Ariel): the preimage of G is not really needed by the OS (it only needs R, F, and the
452+
// new root R', whose opening contains the hash of G). Change this to not contains or delete
453+
// this test after making request_paths_and_commit_block_request stricter.
454+
assert!(
455+
response.patricia_proofs.classes_trie_proof.contains_key(&node_g_hash),
456+
"missing bottom of a new edge node in a proof",
457+
);
458+
}
459+
460+
/// Flow overview:
461+
/// 1. Commit block 0 with three class leaves that form this Patricia topology:
462+
/// ```text
463+
/// R
464+
/// |
465+
/// T
466+
/// / \
467+
/// E F
468+
/// / \ \
469+
/// A B D
470+
/// ```
471+
/// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
472+
/// and requesting witnesses only for the deleted key.
473+
/// 3. Assert the returned classes-trie proof contains node `E` (this will allow verifying that the
474+
/// new edge's bottom is not an edge).
475+
#[tokio::test]
476+
async fn test_bottom_of_new_edge_to_a_binary_unmodified_subtree_is_present() {
477+
let class_hash_a = ClassHash(0_u64.into());
478+
let class_hash_b = ClassHash(1_u64.into());
479+
let class_hash_d = ClassHash(3_u64.into());
480+
481+
let compiled_class_hash_a_felt = 100_u64.into();
482+
let compiled_class_hash_b_felt = 101_u64.into();
483+
let compiled_class_hash_d_felt = 102_u64.into();
484+
485+
let mut committer = new_test_committer().await;
486+
let height_0 = 0;
487+
let height_1 = 1;
488+
let block_0_state_diff = ThinStateDiff {
489+
class_hash_to_compiled_class_hash: indexmap! {
490+
class_hash_a => CompiledClassHash(compiled_class_hash_a_felt),
491+
class_hash_b => CompiledClassHash(compiled_class_hash_b_felt),
492+
class_hash_d => CompiledClassHash(compiled_class_hash_d_felt),
493+
},
494+
..Default::default()
495+
};
496+
let block_1_state_diff = ThinStateDiff {
497+
class_hash_to_compiled_class_hash: indexmap! {
498+
class_hash_d => CompiledClassHash(0_u64.into()),
499+
},
500+
..Default::default()
501+
};
502+
let accessed_keys = AccessedKeys {
503+
accessed_class_hashes: BTreeSet::from([class_hash_d]),
504+
..Default::default()
505+
};
506+
507+
committer
508+
.commit_block(CommitBlockRequest {
509+
state_diff: block_0_state_diff.clone(),
510+
state_diff_commitment: Some(calculate_state_diff_hash(&block_0_state_diff)),
511+
height: BlockNumber(height_0),
512+
})
513+
.await
514+
.unwrap();
515+
516+
let response = committer
517+
.read_paths_and_commit_block(read_paths_and_commit_block_request(
518+
block_1_state_diff.clone(),
519+
Some(calculate_state_diff_hash(&block_1_state_diff)),
520+
height_1,
521+
accessed_keys,
522+
))
523+
.await
524+
.unwrap();
525+
526+
let leaf_a_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
527+
compiled_class_hash_a_felt,
528+
));
529+
let leaf_b_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
530+
compiled_class_hash_b_felt,
531+
));
532+
let node_e_hash = TreeHashFunctionImpl::compute_node_hash(&NodeData::<
533+
CommitterCompiledClassHash,
534+
HashOutput,
535+
>::Binary(BinaryData {
536+
left_data: leaf_a_hash,
537+
right_data: leaf_b_hash,
538+
}));
539+
540+
assert!(
541+
response.patricia_proofs.classes_trie_proof.contains_key(&node_e_hash),
542+
"missing bottom of a new edge node in a proof",
543+
);
544+
}

0 commit comments

Comments
 (0)