1
1
use crate :: utils:: * ;
2
2
use function_name:: named;
3
3
use light_compressed_account:: indexer_event:: event:: BatchNullifyContext ;
4
- use light_merkle_tree_reference :: MerkleTree ;
4
+ use light_compressed_account :: TreeType ;
5
5
use light_hasher:: Poseidon ;
6
+ use light_merkle_tree_reference:: MerkleTree ;
6
7
use photon_indexer:: common:: typedefs:: account:: AccountData ;
7
8
use photon_indexer:: common:: typedefs:: account:: { Account , AccountContext , AccountWithContext } ;
8
9
use photon_indexer:: common:: typedefs:: bs64_string:: Base64String ;
@@ -75,7 +76,7 @@ impl Default for StateUpdateConfig {
75
76
fn default ( ) -> Self {
76
77
Self {
77
78
in_accounts : CollectionConfig :: new ( 0 , 5 , 0.0 ) ,
78
- out_accounts : CollectionConfig :: new ( 0 , 5 , 1.0 ) ,
79
+ out_accounts : CollectionConfig :: new ( 1 , 5 , 1.0 ) ,
79
80
account_transactions : CollectionConfig :: new ( 0 , 3 , 0.0 ) ,
80
81
transactions : CollectionConfig :: new ( 0 , 2 , 0.0 ) ,
81
82
leaf_nullifications : CollectionConfig :: new ( 0 , 3 , 0.0 ) ,
@@ -151,7 +152,11 @@ fn get_rnd_state_update(
151
152
seq : Some ( UnsignedInteger ( base_seq + i as u64 ) ) ,
152
153
slot_created : UnsignedInteger ( slot) ,
153
154
} ,
154
- context : AccountContext :: default ( ) ,
155
+ context : AccountContext {
156
+ tree_type : TreeType :: StateV1 as u16 ,
157
+ queue : tree_info. queue . into ( ) ,
158
+ ..Default :: default ( )
159
+ } ,
155
160
} ;
156
161
state_update. out_accounts . push ( account) ;
157
162
}
@@ -381,67 +386,106 @@ async fn assert_state_tree_root(
381
386
) -> Result < ( ) , Box < dyn std:: error:: Error > > {
382
387
use photon_indexer:: dao:: generated:: state_trees;
383
388
use sea_orm:: ColumnTrait ;
384
-
385
389
if state_update. out_accounts . is_empty ( ) {
386
390
println ! ( "✅ No output accounts - skipping state tree root verification" ) ;
387
391
return Ok ( ( ) ) ;
388
392
}
389
393
390
394
// Get the tree pubkey from the first output account (all should use same tree)
391
- let tree_pubkey_bytes = state_update. out_accounts [ 0 ] . account . tree . 0 . to_bytes ( ) . to_vec ( ) ;
392
-
393
- // Append all new account hashes to reference tree
395
+ let tree_pubkey_bytes = state_update. out_accounts [ 0 ]
396
+ . account
397
+ . tree
398
+ . 0
399
+ . to_bytes ( )
400
+ . to_vec ( ) ;
401
+
402
+ println ! ( "Output Account Hashes:" ) ;
394
403
for account_with_context in & state_update. out_accounts {
395
- let account_hash_bytes = account_with_context. account . hash . 0 . to_vec ( ) ;
396
- let mut hash_array = [ 0u8 ; 32 ] ;
397
- hash_array. copy_from_slice ( & account_hash_bytes) ;
398
- reference_tree. append ( & hash_array) ?;
404
+ let account_hash = hex:: encode ( & account_with_context. account . hash . 0 ) ;
405
+ let leaf_index = account_with_context. account . leaf_index . 0 ;
406
+ println ! ( " Hash({}) at leaf_index {}" , account_hash, leaf_index) ;
399
407
}
400
408
401
- // Get reference tree root
402
- let reference_root = reference_tree. root ( ) ;
403
-
404
- // First, let's see what nodes are actually in the state_trees table
405
- let all_nodes = state_trees:: Entity :: find ( )
409
+ // First, get all leaf nodes from database to verify they match our output accounts
410
+ let leaf_nodes = state_trees:: Entity :: find ( )
406
411
. filter ( state_trees:: Column :: Tree . eq ( tree_pubkey_bytes. clone ( ) ) )
412
+ . filter ( state_trees:: Column :: Level . eq ( 0i64 ) ) // Leaf level
407
413
. all ( db_conn)
408
414
. await ?;
409
415
410
- println ! ( "All nodes in state_trees table for tree {:?}:" , hex:: encode( & tree_pubkey_bytes) ) ;
411
- for node in & all_nodes {
412
- println ! ( " node_idx: {}, level: {}, leaf_idx: {:?}, seq: {:?}, hash: {:?}" ,
413
- node. node_idx, node. level, node. leaf_idx, node. seq, hex:: encode( & node. hash) ) ;
416
+ println ! ( "Database Leaf Hashes:" ) ;
417
+ for leaf in & leaf_nodes {
418
+ println ! ( " Hash({}) at leaf_idx={:?}" , hex:: encode( & leaf. hash) , leaf. leaf_idx) ;
414
419
}
415
420
416
- if all_nodes. is_empty ( ) {
417
- println ! ( "✅ No state tree nodes found - this might be expected for the test configuration" ) ;
418
- return Ok ( ( ) ) ;
421
+ // Assert that all our new account hashes are present as leaf nodes in the database
422
+ for account_with_context in & state_update. out_accounts {
423
+ let account_hash = hex:: encode ( & account_with_context. account . hash . 0 ) ;
424
+ let leaf_index = account_with_context. account . leaf_index . 0 ;
425
+
426
+ let found_leaf = leaf_nodes. iter ( ) . find ( |leaf| {
427
+ leaf. leaf_idx == Some ( leaf_index as i64 ) &&
428
+ hex:: encode ( & leaf. hash ) == account_hash
429
+ } ) ;
430
+
431
+ assert ! ( found_leaf. is_some( ) ,
432
+ "Account hash {} at leaf_index {} not found in database leaf nodes" ,
433
+ account_hash, leaf_index) ;
419
434
}
435
+ println ! ( "✅ All account hashes verified as leaf nodes in database" ) ;
420
436
421
- // Find the root node (highest level)
422
- let max_level = all_nodes. iter ( ) . map ( |node| node. level ) . max ( ) . unwrap_or ( 0 ) ;
423
- let root_nodes: Vec < _ > = all_nodes. iter ( ) . filter ( |node| node. level == max_level) . collect ( ) ;
424
-
425
- if root_nodes. len ( ) != 1 {
426
- println ! ( "⚠️ Multiple or no root nodes found at level {}: {:?}" , max_level, root_nodes. len( ) ) ;
427
- // For now, just skip the root verification
428
- return Ok ( ( ) ) ;
437
+ // Construct reference tree from output accounts directly
438
+ // Find the maximum leaf index to determine tree size needed
439
+ let max_leaf_idx = state_update. out_accounts
440
+ . iter ( )
441
+ . map ( |acc| acc. account . leaf_index . 0 )
442
+ . max ( )
443
+ . unwrap_or ( 0 ) ;
444
+
445
+ println ! ( "Constructing reference tree up to leaf index {}" , max_leaf_idx) ;
446
+
447
+ // Append leaves to reference tree in the correct positions
448
+ // Fill with zero hashes for missing leaves, actual account hashes for present ones
449
+ for i in 0 ..=max_leaf_idx {
450
+ let leaf_hash = state_update. out_accounts
451
+ . iter ( )
452
+ . find ( |acc| acc. account . leaf_index . 0 == i)
453
+ . map ( |acc| acc. account . hash . 0 )
454
+ . unwrap_or ( [ 0u8 ; 32 ] ) ; // Zero hash for missing leaves
455
+
456
+ reference_tree. append ( & leaf_hash) ?;
429
457
}
430
458
459
+ // Get reference tree root after construction
460
+ let reference_root = reference_tree. root ( ) ;
461
+ println ! ( "Reference tree root: {}" , hex:: encode( & reference_root) ) ;
462
+
463
+ // Get database root node for comparison
464
+ let all_nodes = state_trees:: Entity :: find ( )
465
+ . filter ( state_trees:: Column :: Tree . eq ( tree_pubkey_bytes. clone ( ) ) )
466
+ . all ( db_conn)
467
+ . await ?;
468
+
469
+ let max_level = all_nodes. iter ( ) . map ( |node| node. level ) . max ( ) . unwrap_or ( 0 ) ;
470
+ let root_nodes: Vec < _ > = all_nodes
471
+ . iter ( )
472
+ . filter ( |node| node. level == max_level)
473
+ . collect ( ) ;
474
+
475
+ assert_eq ! ( root_nodes. len( ) , 1 , "Expected exactly 1 root node, found {}" , root_nodes. len( ) ) ;
476
+
431
477
let root_node = root_nodes[ 0 ] ;
432
478
let mut db_root_array = [ 0u8 ; 32 ] ;
433
479
db_root_array. copy_from_slice ( & root_node. hash ) ;
480
+ println ! ( "Database root: {}" , hex:: encode( & db_root_array) ) ;
434
481
435
482
assert_eq ! (
436
483
reference_root, db_root_array,
437
- "State tree root mismatch!\n Reference: {:? }\n Database: {:? }" ,
438
- reference_root, db_root_array
484
+ "State tree root mismatch!\n Reference: {}\n Database: {}" ,
485
+ hex :: encode ( & reference_root) , hex :: encode ( & db_root_array)
439
486
) ;
440
-
441
- println ! (
442
- "✅ Successfully verified state tree root matches reference implementation"
443
- ) ;
444
- println ! ( "Tree root: {:?}" , reference_root) ;
487
+
488
+ println ! ( "✅ State tree root verification successful!" ) ;
445
489
446
490
Ok ( ( ) )
447
491
}
@@ -523,10 +567,10 @@ async fn test_output_accounts(#[values(DatabaseBackend::Sqlite)] db_backend: Dat
523
567
let mut rng = StdRng :: seed_from_u64 ( seed) ;
524
568
525
569
// Initialize reference Merkle tree for state tree root verification
526
- let tree_info = TreeInfo :: get ( TEST_TREE_PUBKEY_STR )
527
- . expect ( "Test tree should exist in QUEUE_TREE_MAPPING" ) ;
570
+ let tree_info =
571
+ TreeInfo :: get ( TEST_TREE_PUBKEY_STR ) . expect ( "Test tree should exist in QUEUE_TREE_MAPPING" ) ;
528
572
let tree_height = tree_info. height as usize ;
529
- let mut reference_tree = MerkleTree :: < Poseidon > :: new ( tree_height , 0 ) ;
573
+ let mut reference_tree = MerkleTree :: < Poseidon > :: new ( 26 , 0 ) ;
530
574
531
575
// Test that the new config structure works correctly
532
576
let config = StateUpdateConfig :: default ( ) ;
@@ -536,7 +580,7 @@ async fn test_output_accounts(#[values(DatabaseBackend::Sqlite)] db_backend: Dat
536
580
assert_eq ! ( config. in_accounts. max_entries, 5 ) ;
537
581
assert_eq ! ( config. in_accounts. probability, 0.0 ) ;
538
582
539
- assert_eq ! ( config. out_accounts. min_entries, 0 ) ;
583
+ assert_eq ! ( config. out_accounts. min_entries, 1 ) ;
540
584
assert_eq ! ( config. out_accounts. max_entries, 5 ) ;
541
585
assert_eq ! ( config. out_accounts. probability, 1.0 ) ;
542
586
0 commit comments