@@ -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,9 +36,17 @@ 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:: {
40+ BinaryData ,
41+ EdgeData ,
42+ EdgePath ,
43+ EdgePathLength ,
44+ NodeData ,
45+ PathToBottom ,
46+ } ;
3347use starknet_patricia:: patricia_merkle_tree:: node_data:: leaf:: Leaf ;
3448use starknet_patricia:: patricia_merkle_tree:: storage_proof_verification:: verify_patricia_proof;
35- use starknet_patricia:: patricia_merkle_tree:: types:: NodeIndex ;
49+ use starknet_patricia:: patricia_merkle_tree:: types:: { NodeIndex , SubTreeHeight } ;
3650use starknet_patricia:: patricia_merkle_tree:: updated_skeleton_tree:: hash_function:: TreeHashFunction ;
3751
3852use crate :: committer:: committer_test:: { new_test_committer, ApolloTestCommitter } ;
@@ -359,3 +373,179 @@ async fn revert_removes_witnesses_and_digest() {
359373 assert_witnesses_and_digest_absent ( & mut committer, BlockNumber ( height_1) ) . await ;
360374 assert_eq ! ( committer. offset, BlockNumber ( height_1) ) ;
361375}
376+
377+ /// Flow overview:
378+ /// 1. Commit block 0 with three class leaves that form this Patricia topology:
379+ /// ```text
380+ /// R
381+ /// / \
382+ /// E F
383+ /// | |
384+ /// G |
385+ /// / \ \
386+ /// A B D
387+ /// ```
388+ /// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
389+ /// and requesting witnesses only for the deleted key.
390+ /// 3. Assert the returned classes-trie proof contains node `G` (not strictly necessary, see comment
391+ /// below).
392+ #[ tokio:: test]
393+ async fn test_bottom_of_new_edge_to_an_unmoidifed_subtree_is_present ( ) {
394+ // Set the two leftmost and the rightmost leaves.
395+ let class_hash_a = ClassHash ( 0_u64 . into ( ) ) ;
396+ let class_hash_b = ClassHash ( 1_u64 . into ( ) ) ;
397+ let class_hash_d = ClassHash ( PATRICIA_KEY_UPPER_BOUND_FELT - 1_u64 ) ;
398+
399+ let compiled_class_hash_a_felt = 100_u64 . into ( ) ;
400+ let compiled_class_hash_b_felt = 101_u64 . into ( ) ;
401+ let compiled_class_hash_d_felt = 102_u64 . into ( ) ;
402+
403+ let mut committer = new_test_committer ( ) . await ;
404+ let height_0 = 0 ;
405+ let height_1 = 1 ;
406+ let block_0_state_diff = ThinStateDiff {
407+ class_hash_to_compiled_class_hash : indexmap ! {
408+ class_hash_a => CompiledClassHash ( compiled_class_hash_a_felt) ,
409+ class_hash_b => CompiledClassHash ( compiled_class_hash_b_felt) ,
410+ class_hash_d => CompiledClassHash ( compiled_class_hash_d_felt) ,
411+ } ,
412+ ..Default :: default ( )
413+ } ;
414+ let block_1_state_diff = ThinStateDiff {
415+ class_hash_to_compiled_class_hash : indexmap ! {
416+ class_hash_d => CompiledClassHash ( 0_u64 . into( ) ) ,
417+ } ,
418+ ..Default :: default ( )
419+ } ;
420+ let accessed_keys = AccessedKeys {
421+ accessed_class_hashes : BTreeSet :: from ( [ class_hash_d] ) ,
422+ ..Default :: default ( )
423+ } ;
424+
425+ committer
426+ . commit_block ( CommitBlockRequest {
427+ state_diff : block_0_state_diff. clone ( ) ,
428+ state_diff_commitment : Some ( calculate_state_diff_hash ( & block_0_state_diff) ) ,
429+ height : BlockNumber ( height_0) ,
430+ } )
431+ . await
432+ . unwrap ( ) ;
433+
434+ let response = committer
435+ . read_paths_and_commit_block ( read_paths_and_commit_block_request (
436+ block_1_state_diff. clone ( ) ,
437+ Some ( calculate_state_diff_hash ( & block_1_state_diff) ) ,
438+ height_1,
439+ accessed_keys,
440+ ) )
441+ . await
442+ . unwrap ( ) ;
443+
444+ let leaf_a_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
445+ compiled_class_hash_a_felt,
446+ ) ) ;
447+ let leaf_b_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
448+ compiled_class_hash_b_felt,
449+ ) ) ;
450+ let node_g_hash = TreeHashFunctionImpl :: compute_node_hash ( & NodeData :: <
451+ CommitterCompiledClassHash ,
452+ HashOutput ,
453+ > :: Binary ( BinaryData {
454+ left_data : leaf_a_hash,
455+ right_data : leaf_b_hash,
456+ } ) ) ;
457+
458+ // TODO(Ariel): the preimage of G is not really needed by the OS (it only needs R, F, and the
459+ // new root R', whose opening contains the hash of G). Change this to not contains or delete
460+ // this test after making request_paths_and_commit_block_request stricter.
461+ assert ! (
462+ response. patricia_proofs. classes_trie_proof. contains_key( & node_g_hash) ,
463+ "missing bottom of a new edge node in a proof" ,
464+ ) ;
465+ }
466+
467+ /// Flow overview:
468+ /// 1. Commit block 0 with three class leaves that form this Patricia topology:
469+ /// ```text
470+ /// R
471+ /// |
472+ /// T
473+ /// / \
474+ /// E F
475+ /// / \ \
476+ /// A B D
477+ /// ```
478+ /// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
479+ /// and requesting witnesses only for the deleted key.
480+ /// 3. Assert the returned classes-trie proof contains node `E` (this will allow verifying that the
481+ /// new edge's bottom is not an edge).
482+ #[ tokio:: test]
483+ async fn test_bottom_of_new_edge_to_a_binary_unmodified_subtree_is_present ( ) {
484+ let class_hash_a = ClassHash ( 0_u64 . into ( ) ) ;
485+ let class_hash_b = ClassHash ( 1_u64 . into ( ) ) ;
486+ let class_hash_d = ClassHash ( 3_u64 . into ( ) ) ;
487+
488+ let compiled_class_hash_a_felt = 100_u64 . into ( ) ;
489+ let compiled_class_hash_b_felt = 101_u64 . into ( ) ;
490+ let compiled_class_hash_d_felt = 102_u64 . into ( ) ;
491+
492+ let mut committer = new_test_committer ( ) . await ;
493+ let height_0 = 0 ;
494+ let height_1 = 1 ;
495+ let block_0_state_diff = ThinStateDiff {
496+ class_hash_to_compiled_class_hash : indexmap ! {
497+ class_hash_a => CompiledClassHash ( compiled_class_hash_a_felt) ,
498+ class_hash_b => CompiledClassHash ( compiled_class_hash_b_felt) ,
499+ class_hash_d => CompiledClassHash ( compiled_class_hash_d_felt) ,
500+ } ,
501+ ..Default :: default ( )
502+ } ;
503+ let block_1_state_diff = ThinStateDiff {
504+ class_hash_to_compiled_class_hash : indexmap ! {
505+ class_hash_d => CompiledClassHash ( 0_u64 . into( ) ) ,
506+ } ,
507+ ..Default :: default ( )
508+ } ;
509+ let accessed_keys = AccessedKeys {
510+ accessed_class_hashes : BTreeSet :: from ( [ class_hash_d] ) ,
511+ ..Default :: default ( )
512+ } ;
513+
514+ committer
515+ . commit_block ( CommitBlockRequest {
516+ state_diff : block_0_state_diff. clone ( ) ,
517+ state_diff_commitment : Some ( calculate_state_diff_hash ( & block_0_state_diff) ) ,
518+ height : BlockNumber ( height_0) ,
519+ } )
520+ . await
521+ . unwrap ( ) ;
522+
523+ let response = committer
524+ . read_paths_and_commit_block ( read_paths_and_commit_block_request (
525+ block_1_state_diff. clone ( ) ,
526+ Some ( calculate_state_diff_hash ( & block_1_state_diff) ) ,
527+ height_1,
528+ accessed_keys,
529+ ) )
530+ . await
531+ . unwrap ( ) ;
532+
533+ let leaf_a_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
534+ compiled_class_hash_a_felt,
535+ ) ) ;
536+ let leaf_b_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
537+ compiled_class_hash_b_felt,
538+ ) ) ;
539+ let node_e_hash = TreeHashFunctionImpl :: compute_node_hash ( & NodeData :: <
540+ CommitterCompiledClassHash ,
541+ HashOutput ,
542+ > :: Binary ( BinaryData {
543+ left_data : leaf_a_hash,
544+ right_data : leaf_b_hash,
545+ } ) ) ;
546+
547+ assert ! (
548+ response. patricia_proofs. classes_trie_proof. contains_key( & node_e_hash) ,
549+ "missing bottom of a new edge node in a proof" ,
550+ ) ;
551+ }
0 commit comments