@@ -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,189 @@ 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 `E`.
391+ #[ tokio:: test]
392+ async fn test_bottom_of_new_edge_to_an_unmoidifed_subtree_is_present ( ) {
393+ // Set the two leftmost and the rightmost leaves.
394+ let class_hash_a = ClassHash ( 0_u64 . into ( ) ) ;
395+ let class_hash_b = ClassHash ( 1_u64 . into ( ) ) ;
396+ let class_hash_d = ClassHash ( PATRICIA_KEY_UPPER_BOUND_FELT - 1_u64 ) ;
397+
398+ let compiled_class_hash_a_felt = 100_u64 . into ( ) ;
399+ let compiled_class_hash_b_felt = 101_u64 . into ( ) ;
400+ let compiled_class_hash_d_felt = 102_u64 . into ( ) ;
401+
402+ let mut committer = new_test_committer ( ) . await ;
403+ let height_0 = 0 ;
404+ let height_1 = 1 ;
405+ let block_0_state_diff = ThinStateDiff {
406+ class_hash_to_compiled_class_hash : indexmap ! {
407+ class_hash_a => CompiledClassHash ( compiled_class_hash_a_felt) ,
408+ class_hash_b => CompiledClassHash ( compiled_class_hash_b_felt) ,
409+ class_hash_d => CompiledClassHash ( compiled_class_hash_d_felt) ,
410+ } ,
411+ ..Default :: default ( )
412+ } ;
413+ let block_1_state_diff = ThinStateDiff {
414+ class_hash_to_compiled_class_hash : indexmap ! {
415+ class_hash_d => CompiledClassHash ( 0_u64 . into( ) ) ,
416+ } ,
417+ ..Default :: default ( )
418+ } ;
419+ let accessed_keys = AccessedKeys {
420+ accessed_class_hashes : BTreeSet :: from ( [ class_hash_d] ) ,
421+ ..Default :: default ( )
422+ } ;
423+
424+ committer
425+ . commit_block ( CommitBlockRequest {
426+ state_diff : block_0_state_diff. clone ( ) ,
427+ state_diff_commitment : Some ( calculate_state_diff_hash ( & block_0_state_diff) ) ,
428+ height : BlockNumber ( height_0) ,
429+ } )
430+ . await
431+ . unwrap ( ) ;
432+
433+ let response = committer
434+ . read_paths_and_commit_block ( read_paths_and_commit_block_request (
435+ block_1_state_diff. clone ( ) ,
436+ Some ( calculate_state_diff_hash ( & block_1_state_diff) ) ,
437+ height_1,
438+ accessed_keys,
439+ ) )
440+ . await
441+ . unwrap ( ) ;
442+
443+ let leaf_a_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
444+ compiled_class_hash_a_felt,
445+ ) ) ;
446+ let leaf_b_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
447+ compiled_class_hash_b_felt,
448+ ) ) ;
449+ let node_g_hash = TreeHashFunctionImpl :: compute_node_hash ( & NodeData :: <
450+ CommitterCompiledClassHash ,
451+ HashOutput ,
452+ > :: Binary ( BinaryData {
453+ left_data : leaf_a_hash,
454+ right_data : leaf_b_hash,
455+ } ) ) ;
456+ // Keys 0 and 1 place G one level above the leaves; E is the root's left child.
457+ let path_e_to_g = PathToBottom :: new (
458+ EdgePath :: default ( ) ,
459+ EdgePathLength :: new ( SubTreeHeight :: ACTUAL_HEIGHT . 0 - 1 ) . expect ( "Illegal edge path length" ) ,
460+ )
461+ . expect ( "Illegal path from E to G" ) ;
462+ let node_e_hash = TreeHashFunctionImpl :: compute_node_hash ( & NodeData :: <
463+ CommitterCompiledClassHash ,
464+ HashOutput ,
465+ > :: Edge ( EdgeData {
466+ bottom_data : node_g_hash,
467+ path_to_bottom : path_e_to_g,
468+ } ) ) ;
469+
470+ assert ! (
471+ response. patricia_proofs. classes_trie_proof. contains_key( & node_e_hash) ,
472+ "missing bottom of a new edge node in a proof" ,
473+ ) ;
474+ }
475+
476+ /// Flow overview:
477+ /// 1. Commit block 0 with three class leaves that form this Patricia topology:
478+ /// ```text
479+ /// R
480+ /// |
481+ /// T
482+ /// / \
483+ /// E F
484+ /// / \ \
485+ /// A B D
486+ /// ```
487+ /// 2. Commit block 1 via [crate::committer::Committer::read_paths_and_commit_block], deleting `D`
488+ /// and requesting witnesses only for the deleted key.
489+ /// 3. Assert the returned classes-trie proof contains node `E`. In this test, E is not the bottom
490+ /// of an existing edge, so it must be included in the proof to become convinced of the new
491+ /// tree's validity.
492+ #[ tokio:: test]
493+ async fn test_bottom_of_new_edge_to_a_binary_unmodified_subtree_is_present ( ) {
494+ let class_hash_a = ClassHash ( 0_u64 . into ( ) ) ;
495+ let class_hash_b = ClassHash ( 1_u64 . into ( ) ) ;
496+ let class_hash_d = ClassHash ( 3_u64 . into ( ) ) ;
497+
498+ let compiled_class_hash_a_felt = 100_u64 . into ( ) ;
499+ let compiled_class_hash_b_felt = 101_u64 . into ( ) ;
500+ let compiled_class_hash_d_felt = 102_u64 . into ( ) ;
501+
502+ let mut committer = new_test_committer ( ) . await ;
503+ let height_0 = 0 ;
504+ let height_1 = 1 ;
505+ let block_0_state_diff = ThinStateDiff {
506+ class_hash_to_compiled_class_hash : indexmap ! {
507+ class_hash_a => CompiledClassHash ( compiled_class_hash_a_felt) ,
508+ class_hash_b => CompiledClassHash ( compiled_class_hash_b_felt) ,
509+ class_hash_d => CompiledClassHash ( compiled_class_hash_d_felt) ,
510+ } ,
511+ ..Default :: default ( )
512+ } ;
513+ let block_1_state_diff = ThinStateDiff {
514+ class_hash_to_compiled_class_hash : indexmap ! {
515+ class_hash_d => CompiledClassHash ( 0_u64 . into( ) ) ,
516+ } ,
517+ ..Default :: default ( )
518+ } ;
519+ let accessed_keys = AccessedKeys {
520+ accessed_class_hashes : BTreeSet :: from ( [ class_hash_d] ) ,
521+ ..Default :: default ( )
522+ } ;
523+
524+ committer
525+ . commit_block ( CommitBlockRequest {
526+ state_diff : block_0_state_diff. clone ( ) ,
527+ state_diff_commitment : Some ( calculate_state_diff_hash ( & block_0_state_diff) ) ,
528+ height : BlockNumber ( height_0) ,
529+ } )
530+ . await
531+ . unwrap ( ) ;
532+
533+ let response = committer
534+ . read_paths_and_commit_block ( read_paths_and_commit_block_request (
535+ block_1_state_diff. clone ( ) ,
536+ Some ( calculate_state_diff_hash ( & block_1_state_diff) ) ,
537+ height_1,
538+ accessed_keys,
539+ ) )
540+ . await
541+ . unwrap ( ) ;
542+
543+ let leaf_a_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
544+ compiled_class_hash_a_felt,
545+ ) ) ;
546+ let leaf_b_hash = TreeHashFunctionImpl :: compute_leaf_hash ( & CommitterCompiledClassHash (
547+ compiled_class_hash_b_felt,
548+ ) ) ;
549+ let node_e_hash = TreeHashFunctionImpl :: compute_node_hash ( & NodeData :: <
550+ CommitterCompiledClassHash ,
551+ HashOutput ,
552+ > :: Binary ( BinaryData {
553+ left_data : leaf_a_hash,
554+ right_data : leaf_b_hash,
555+ } ) ) ;
556+
557+ assert ! (
558+ response. patricia_proofs. classes_trie_proof. contains_key( & node_e_hash) ,
559+ "missing bottom of a new edge node in a proof" ,
560+ ) ;
561+ }
0 commit comments