2026-03-04 18:42:33 +03:00
#![ expect(
2026-03-24 13:01:06 +11:00
clippy ::shadow_unrelated ,
2026-03-04 18:42:33 +03:00
clippy ::tests_outside_test_module ,
reason = " We don't care about these in tests "
) ]
2026-02-10 15:29:23 +02:00
use std ::time ::Duration ;
2026-03-24 12:04:13 +11:00
use anyhow ::{ Context as _ , Result } ;
2026-03-04 18:42:33 +03:00
use indexer_service_rpc ::RpcClient as _ ;
2026-03-24 12:04:13 +11:00
use integration_tests ::{
2026-05-14 21:19:25 -04:00
TIME_TO_WAIT_FOR_BLOCK_SECONDS , TestContext , private_mention , public_mention ,
verify_commitment_is_in_state ,
2026-03-24 12:04:13 +11:00
} ;
2026-02-10 14:03:56 +02:00
use log ::info ;
2026-03-24 12:04:13 +11:00
use nssa ::AccountId ;
2026-05-14 21:19:25 -04:00
use wallet ::{
account ::Label ,
cli ::{ CliAccountMention , Command , programs ::native_token_transfer ::AuthTransferSubcommand } ,
} ;
2026-02-10 15:29:23 +02:00
2026-04-09 14:29:36 +02:00
/// Maximum time to wait for the indexer to catch up to the sequencer.
2026-04-09 17:10:40 +02:00
const L2_TO_L1_TIMEOUT_MILLIS : u64 = 180_000 ;
2026-02-10 14:03:56 +02:00
2026-04-09 14:29:36 +02:00
/// Poll the indexer until its last finalized block id reaches the sequencer's
2026-05-14 21:19:25 -04:00
/// current last block id or until [`L2_TO_L1_TIMEOUT_MILLIS`] elapses.
/// Returns the last indexer block id observed.
async fn wait_for_indexer_to_catch_up ( ctx : & TestContext ) -> Result < u64 > {
2026-04-09 15:04:15 +02:00
let timeout = Duration ::from_millis ( L2_TO_L1_TIMEOUT_MILLIS ) ;
2026-05-14 21:19:25 -04:00
let block_id_to_catch_up =
sequencer_service_rpc ::RpcClient ::get_last_block_id ( ctx . sequencer_client ( ) ) . await ? ;
2026-04-09 15:04:15 +02:00
let mut last_ind : u64 = 1 ;
let inner = async {
loop {
let ind = ctx
. indexer_client ( )
. get_last_finalized_block_id ( )
2026-05-14 21:19:25 -04:00
. await ?
. unwrap_or ( 0 ) ;
2026-04-09 15:04:15 +02:00
last_ind = ind ;
2026-05-14 21:19:25 -04:00
if ind > = block_id_to_catch_up {
let last_seq =
sequencer_service_rpc ::RpcClient ::get_last_block_id ( ctx . sequencer_client ( ) )
. await ? ;
info! (
" Indexer caught up. Indexer last block id: {ind}. Current sequencer last block id: {last_seq} "
) ;
return Ok ( ind ) ;
2026-04-09 15:04:15 +02:00
}
tokio ::time ::sleep ( Duration ::from_secs ( 2 ) ) . await ;
2026-04-09 14:29:36 +02:00
}
2026-04-09 15:04:15 +02:00
} ;
2026-04-09 15:45:54 +02:00
tokio ::time ::timeout ( timeout , inner )
. await
2026-05-14 21:19:25 -04:00
. with_context ( | | {
format! (
" Indexer failed to catch up within {L2_TO_L1_TIMEOUT_MILLIS} milliseconds. Last indexer block id observed: {last_ind}, but needed to catch up to at least {block_id_to_catch_up} "
)
} ) ?
2026-04-09 14:29:36 +02:00
}
2026-04-03 15:50:24 +03:00
#[ tokio::test ]
2026-02-10 14:03:56 +02:00
async fn indexer_test_run ( ) -> Result < ( ) > {
let ctx = TestContext ::new ( ) . await ? ;
2026-05-14 21:19:25 -04:00
let last_block_indexer = wait_for_indexer_to_catch_up ( & ctx ) . await ? ;
2026-02-10 14:03:56 +02:00
2026-03-13 22:38:23 +03:00
let last_block_seq =
sequencer_service_rpc ::RpcClient ::get_last_block_id ( ctx . sequencer_client ( ) ) . await ? ;
2026-02-10 14:03:56 +02:00
info! ( " Last block on seq now is {last_block_seq} " ) ;
info! ( " Last block on ind now is {last_block_indexer} " ) ;
2026-05-14 21:19:25 -04:00
assert! ( last_block_indexer > 0 ) ;
2026-02-10 14:03:56 +02:00
Ok ( ( ) )
}
2026-02-10 15:29:23 +02:00
2026-04-03 15:50:24 +03:00
#[ tokio::test ]
2026-02-10 15:29:23 +02:00
async fn indexer_block_batching ( ) -> Result < ( ) > {
let ctx = TestContext ::new ( ) . await ? ;
info! ( " Waiting for indexer to parse blocks " ) ;
2026-05-14 21:19:25 -04:00
let last_block_indexer = wait_for_indexer_to_catch_up ( & ctx ) . await ? ;
2026-02-10 15:29:23 +02:00
info! ( " Last block on ind now is {last_block_indexer} " ) ;
2026-05-14 21:19:25 -04:00
assert! ( last_block_indexer > 0 ) ;
2026-02-10 15:29:23 +02:00
2026-03-04 21:54:23 +03:00
// Getting wide batch to fit all blocks (from latest backwards)
let mut block_batch = ctx . indexer_client ( ) . get_blocks ( None , 100 ) . await . unwrap ( ) ;
// Reverse to check chain consistency from oldest to newest
block_batch . reverse ( ) ;
2026-02-10 15:29:23 +02:00
// Checking chain consistency
let mut prev_block_hash = block_batch . first ( ) . unwrap ( ) . header . hash ;
for block in & block_batch [ 1 .. ] {
assert_eq! ( block . header . prev_block_hash , prev_block_hash ) ;
info! ( " Block {} chain-consistent " , block . header . block_id ) ;
prev_block_hash = block . header . hash ;
2026-02-10 15:54:57 +02:00
}
2026-02-10 15:29:23 +02:00
Ok ( ( ) )
}
2026-04-03 15:50:24 +03:00
#[ tokio::test ]
2026-02-10 15:29:23 +02:00
async fn indexer_state_consistency ( ) -> Result < ( ) > {
let mut ctx = TestContext ::new ( ) . await ? ;
let command = Command ::AuthTransfer ( AuthTransferSubcommand ::Send {
2026-05-14 21:19:25 -04:00
from : public_mention ( ctx . existing_public_accounts ( ) [ 0 ] ) ,
to : Some ( public_mention ( ctx . existing_public_accounts ( ) [ 1 ] ) ) ,
2026-02-10 15:29:23 +02:00
to_npk : None ,
2026-02-17 08:06:16 +02:00
to_vpk : None ,
2026-04-23 23:31:21 -03:00
to_identifier : Some ( 0 ) ,
2026-02-10 15:29:23 +02:00
amount : 100 ,
} ) ;
wallet ::cli ::execute_subcommand ( ctx . wallet_mut ( ) , command ) . await ? ;
info! ( " Waiting for next block creation " ) ;
tokio ::time ::sleep ( Duration ::from_secs ( TIME_TO_WAIT_FOR_BLOCK_SECONDS ) ) . await ;
info! ( " Checking correct balance move " ) ;
2026-03-13 22:38:23 +03:00
let acc_1_balance = sequencer_service_rpc ::RpcClient ::get_account_balance (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 0 ] ,
)
. await ? ;
let acc_2_balance = sequencer_service_rpc ::RpcClient ::get_account_balance (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 1 ] ,
)
. await ? ;
2026-02-10 15:29:23 +02:00
info! ( " Balance of sender: {acc_1_balance:#?} " ) ;
info! ( " Balance of receiver: {acc_2_balance:#?} " ) ;
2026-03-13 22:38:23 +03:00
assert_eq! ( acc_1_balance , 9900 ) ;
assert_eq! ( acc_2_balance , 20100 ) ;
2026-02-10 15:29:23 +02:00
2026-03-16 15:20:00 +11:00
let from : AccountId = ctx . existing_private_accounts ( ) [ 0 ] ;
let to : AccountId = ctx . existing_private_accounts ( ) [ 1 ] ;
let command = Command ::AuthTransfer ( AuthTransferSubcommand ::Send {
2026-05-14 21:19:25 -04:00
from : private_mention ( from ) ,
to : Some ( private_mention ( to ) ) ,
2026-03-16 15:20:00 +11:00
to_npk : None ,
to_vpk : None ,
2026-04-23 23:31:21 -03:00
to_identifier : Some ( 0 ) ,
2026-03-16 15:20:00 +11:00
amount : 100 ,
} ) ;
wallet ::cli ::execute_subcommand ( ctx . wallet_mut ( ) , command ) . await ? ;
info! ( " Waiting for next block creation " ) ;
tokio ::time ::sleep ( Duration ::from_secs ( TIME_TO_WAIT_FOR_BLOCK_SECONDS ) ) . await ;
let new_commitment1 = ctx
. wallet ( )
. get_private_account_commitment ( from )
. context ( " Failed to get private account commitment for sender " ) ? ;
assert! ( verify_commitment_is_in_state ( new_commitment1 , ctx . sequencer_client ( ) ) . await ) ;
let new_commitment2 = ctx
. wallet ( )
. get_private_account_commitment ( to )
. context ( " Failed to get private account commitment for receiver " ) ? ;
assert! ( verify_commitment_is_in_state ( new_commitment2 , ctx . sequencer_client ( ) ) . await ) ;
info! ( " Successfully transferred privately to owned account " ) ;
2026-02-10 15:29:23 +02:00
info! ( " Waiting for indexer to parse blocks " ) ;
2026-05-14 21:19:25 -04:00
wait_for_indexer_to_catch_up ( & ctx ) . await ? ;
2026-02-10 15:29:23 +02:00
2026-02-10 15:54:57 +02:00
let acc1_ind_state = ctx
. indexer_client ( )
. get_account ( ctx . existing_public_accounts ( ) [ 0 ] . into ( ) )
. await
. unwrap ( ) ;
let acc2_ind_state = ctx
. indexer_client ( )
. get_account ( ctx . existing_public_accounts ( ) [ 1 ] . into ( ) )
. await
. unwrap ( ) ;
2026-02-10 15:29:23 +02:00
info! ( " Checking correct state transition " ) ;
2026-03-13 22:38:23 +03:00
let acc1_seq_state = sequencer_service_rpc ::RpcClient ::get_account (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 0 ] ,
)
. await ? ;
let acc2_seq_state = sequencer_service_rpc ::RpcClient ::get_account (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 1 ] ,
)
. await ? ;
2026-02-10 15:29:23 +02:00
assert_eq! ( acc1_ind_state , acc1_seq_state . into ( ) ) ;
assert_eq! ( acc2_ind_state , acc2_seq_state . into ( ) ) ;
// ToDo: Check private state transition
Ok ( ( ) )
}
2026-03-24 12:04:13 +11:00
2026-04-13 16:01:14 +03:00
#[ tokio::test ]
2026-03-24 12:04:13 +11:00
async fn indexer_state_consistency_with_labels ( ) -> Result < ( ) > {
let mut ctx = TestContext ::new ( ) . await ? ;
// Assign labels to both accounts
2026-05-14 21:19:25 -04:00
let from_label = Label ::new ( " idx-sender-label " ) ;
let to_label = Label ::new ( " idx-receiver-label " ) ;
2026-03-24 12:04:13 +11:00
let label_cmd = Command ::Account ( wallet ::cli ::account ::AccountSubcommand ::Label {
2026-05-14 21:19:25 -04:00
account_id : public_mention ( ctx . existing_public_accounts ( ) [ 0 ] ) ,
2026-03-24 12:04:13 +11:00
label : from_label . clone ( ) ,
} ) ;
wallet ::cli ::execute_subcommand ( ctx . wallet_mut ( ) , label_cmd ) . await ? ;
let label_cmd = Command ::Account ( wallet ::cli ::account ::AccountSubcommand ::Label {
2026-05-14 21:19:25 -04:00
account_id : public_mention ( ctx . existing_public_accounts ( ) [ 1 ] ) ,
label : to_label . clone ( ) ,
2026-03-24 12:04:13 +11:00
} ) ;
wallet ::cli ::execute_subcommand ( ctx . wallet_mut ( ) , label_cmd ) . await ? ;
// Send using labels instead of account IDs
let command = Command ::AuthTransfer ( AuthTransferSubcommand ::Send {
2026-05-14 21:19:25 -04:00
from : CliAccountMention ::Label ( from_label ) ,
to : Some ( CliAccountMention ::Label ( to_label ) ) ,
2026-03-24 12:04:13 +11:00
to_npk : None ,
to_vpk : None ,
2026-04-23 23:31:21 -03:00
to_identifier : Some ( 0 ) ,
2026-03-24 12:04:13 +11:00
amount : 100 ,
} ) ;
wallet ::cli ::execute_subcommand ( ctx . wallet_mut ( ) , command ) . await ? ;
info! ( " Waiting for next block creation " ) ;
tokio ::time ::sleep ( Duration ::from_secs ( TIME_TO_WAIT_FOR_BLOCK_SECONDS ) ) . await ;
let acc_1_balance = sequencer_service_rpc ::RpcClient ::get_account_balance (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 0 ] ,
)
. await ? ;
let acc_2_balance = sequencer_service_rpc ::RpcClient ::get_account_balance (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 1 ] ,
)
. await ? ;
assert_eq! ( acc_1_balance , 9900 ) ;
assert_eq! ( acc_2_balance , 20100 ) ;
info! ( " Waiting for indexer to parse blocks " ) ;
2026-05-14 21:19:25 -04:00
wait_for_indexer_to_catch_up ( & ctx ) . await ? ;
2026-03-24 12:04:13 +11:00
let acc1_ind_state = ctx
. indexer_client ( )
. get_account ( ctx . existing_public_accounts ( ) [ 0 ] . into ( ) )
. await
. unwrap ( ) ;
let acc1_seq_state = sequencer_service_rpc ::RpcClient ::get_account (
ctx . sequencer_client ( ) ,
ctx . existing_public_accounts ( ) [ 0 ] ,
)
. await ? ;
assert_eq! ( acc1_ind_state , acc1_seq_state . into ( ) ) ;
info! ( " Indexer state is consistent after label-based transfer " ) ;
Ok ( ( ) )
}