@@ -10,7 +10,13 @@ use apollo_committer_types::committer_types::{
1010use indexmap:: indexmap;
1111use starknet_api:: block:: BlockNumber ;
1212use 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+ } ;
1420use starknet_api:: hash:: HashOutput ;
1521use starknet_api:: state:: ThinStateDiff ;
1622use 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 } ;
3340use starknet_patricia:: patricia_merkle_tree:: node_data:: leaf:: Leaf ;
3441use starknet_patricia:: patricia_merkle_tree:: storage_proof_verification:: verify_patricia_proof;
3542use 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