Skip to content

Commit b5c134f

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

1 file changed

Lines changed: 189 additions & 1 deletion

File tree

crates/apollo_committer/src/request_paths_and_commit_block_tests.rs

Lines changed: 189 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,184 @@ 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+
//
455+
// For completeness, the OS verifies:
456+
// hash(G_hash, truncated_path) + (len([R',G]) - 1) == E_hash.
457+
// This in turn also proves that G is not an edge node, as it's the bottom of an old
458+
// edge node, without explicitly requesting an opening of E.
459+
assert!(
460+
response.patricia_proofs.classes_trie_proof.contains_key(&node_g_hash),
461+
"missing bottom of a new edge node in a proof",
462+
);
463+
}
464+
465+
/// Flow overview:
466+
/// 1. Commit block 0 with three class leaves that form this Patricia topology:
467+
/// ```text
468+
/// R
469+
/// |
470+
/// T
471+
/// / \
472+
/// E F
473+
/// / \ \
474+
/// A B D
475+
/// ```
476+
/// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
477+
/// and requesting witnesses only for the deleted key.
478+
/// 3. Assert the returned classes-trie proof contains node `E` (this will allow verifying that the
479+
/// new edge's bottom is not an edge).
480+
#[tokio::test]
481+
async fn test_bottom_of_new_edge_which_was_not_bottom_of_an_old_edge_is_present() {
482+
let class_hash_a = ClassHash(0_u64.into());
483+
let class_hash_b = ClassHash(1_u64.into());
484+
let class_hash_d = ClassHash(3_u64.into());
485+
486+
let compiled_class_hash_a_felt = 100_u64.into();
487+
let compiled_class_hash_b_felt = 101_u64.into();
488+
let compiled_class_hash_d_felt = 102_u64.into();
489+
490+
let mut committer = new_test_committer().await;
491+
let height_0 = 0;
492+
let height_1 = 1;
493+
let block_0_state_diff = ThinStateDiff {
494+
class_hash_to_compiled_class_hash: indexmap! {
495+
class_hash_a => CompiledClassHash(compiled_class_hash_a_felt),
496+
class_hash_b => CompiledClassHash(compiled_class_hash_b_felt),
497+
class_hash_d => CompiledClassHash(compiled_class_hash_d_felt),
498+
},
499+
..Default::default()
500+
};
501+
let block_1_state_diff = ThinStateDiff {
502+
class_hash_to_compiled_class_hash: indexmap! {
503+
class_hash_d => CompiledClassHash(0_u64.into()),
504+
},
505+
..Default::default()
506+
};
507+
let accessed_keys = AccessedKeys {
508+
accessed_class_hashes: BTreeSet::from([class_hash_d]),
509+
..Default::default()
510+
};
511+
512+
committer
513+
.commit_block(CommitBlockRequest {
514+
state_diff: block_0_state_diff.clone(),
515+
state_diff_commitment: Some(calculate_state_diff_hash(&block_0_state_diff)),
516+
height: BlockNumber(height_0),
517+
})
518+
.await
519+
.unwrap();
520+
521+
let response = committer
522+
.read_paths_and_commit_block(read_paths_and_commit_block_request(
523+
block_1_state_diff.clone(),
524+
Some(calculate_state_diff_hash(&block_1_state_diff)),
525+
height_1,
526+
accessed_keys,
527+
))
528+
.await
529+
.unwrap();
530+
531+
let leaf_a_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
532+
compiled_class_hash_a_felt,
533+
));
534+
let leaf_b_hash = TreeHashFunctionImpl::compute_leaf_hash(&CommitterCompiledClassHash(
535+
compiled_class_hash_b_felt,
536+
));
537+
let node_e_hash = TreeHashFunctionImpl::compute_node_hash(&NodeData::<
538+
CommitterCompiledClassHash,
539+
HashOutput,
540+
>::Binary(BinaryData {
541+
left_data: leaf_a_hash,
542+
right_data: leaf_b_hash,
543+
}));
544+
545+
assert!(
546+
response.patricia_proofs.classes_trie_proof.contains_key(&node_e_hash),
547+
"missing bottom of a new edge node in a proof",
548+
);
549+
}

0 commit comments

Comments
 (0)