Skip to content

Commit 404fa18

Browse files
committed
feat(FI-734) [icrc index-ng] add status endpoint and change trigger_heatbeat in tests
1 parent a86f044 commit 404fa18

File tree

4 files changed

+76
-31
lines changed

4 files changed

+76
-31
lines changed

rs/rosetta-api/icrc1/index-ng/index-ng.did

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,15 @@ type ListSubaccountsArgs = record {
9999
start: opt SubAccount;
100100
};
101101

102+
type Status = record {
103+
num_blocks_synced : BlockIndex;
104+
};
105+
102106
service : (index_arg: IndexArg) -> {
103107
get_account_transactions : (GetAccountTransactionsArgs) -> (GetTransactionsResult);
104108
get_blocks : (GetBlocksRequest) -> (GetBlocksResponse) query;
105109
icrc1_balance_of : (Account) -> (Tokens) query;
106110
ledger_id : () -> (principal) query;
107111
list_subaccounts : (ListSubaccountsArgs) -> (vec SubAccount) query;
112+
status : () -> (Status) query;
108113
}

rs/rosetta-api/icrc1/index-ng/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,8 @@ pub struct ListSubaccountsArgs {
6969
// in natural order.
7070
pub start: Option<Subaccount>,
7171
}
72+
73+
#[derive(CandidType, Debug, Deserialize, PartialEq, Eq)]
74+
pub struct Status {
75+
pub num_blocks_synced: BlockIndex,
76+
}

rs/rosetta-api/icrc1/index-ng/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ic_icrc1::blocks::{encoded_block_to_generic_block, generic_block_to_encoded_
77
use ic_icrc1::{Block, Operation};
88
use ic_icrc1_index_ng::{
99
GetAccountTransactionsArgs, GetAccountTransactionsResponse, GetAccountTransactionsResult,
10-
IndexArg, ListSubaccountsArgs, TransactionWithId, DEFAULT_MAX_BLOCKS_PER_RESPONSE,
10+
IndexArg, ListSubaccountsArgs, Status, TransactionWithId, DEFAULT_MAX_BLOCKS_PER_RESPONSE,
1111
};
1212
use ic_ledger_core::block::{BlockIndex as BlockIndex64, BlockType, EncodedBlock};
1313
use ic_stable_structures::memory_manager::{MemoryId, VirtualMemory};
@@ -600,6 +600,13 @@ fn icrc1_balance_of(account: Account) -> Nat {
600600
get_balance(account).into()
601601
}
602602

603+
#[query]
604+
#[candid_method(query)]
605+
fn status() -> Status {
606+
let num_blocks_synced = with_blocks(|blocks| blocks.len().into());
607+
Status { num_blocks_synced }
608+
}
609+
603610
#[query]
604611
#[candid_method(query)]
605612
fn list_subaccounts(args: ListSubaccountsArgs) -> Vec<Subaccount> {

rs/rosetta-api/icrc1/index-ng/tests/tests.rs

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use candid::{Decode, Encode, Nat};
22
use ic_base_types::{CanisterId, PrincipalId};
33
use ic_icrc1_index_ng::{
44
GetAccountTransactionsArgs, GetAccountTransactionsResponse, GetAccountTransactionsResult,
5-
GetBlocksResponse, IndexArg, InitArg as IndexInitArg, ListSubaccountsArgs, TransactionWithId,
6-
DEFAULT_MAX_BLOCKS_PER_RESPONSE,
5+
GetBlocksResponse, IndexArg, InitArg as IndexInitArg, ListSubaccountsArgs, Status,
6+
TransactionWithId, DEFAULT_MAX_BLOCKS_PER_RESPONSE,
77
};
88
use ic_icrc1_ledger::{InitArgs as LedgerInitArgs, LedgerArgument};
99
use ic_icrc1_test_utils::{valid_transactions_strategy, CallerTransferArg};
@@ -12,7 +12,7 @@ use ic_state_machine_tests::StateMachine;
1212
use icrc_ledger_types::icrc::generic_metadata_value::MetadataValue as Value;
1313
use icrc_ledger_types::icrc1::account::{Account, Subaccount};
1414
use icrc_ledger_types::icrc1::transfer::{BlockIndex, TransferArg, TransferError};
15-
use icrc_ledger_types::icrc3::blocks::{BlockRange, GenericBlock, GetBlocksRequest};
15+
use icrc_ledger_types::icrc3::blocks::{BlockRange, GetBlocksRequest};
1616
use icrc_ledger_types::icrc3::transactions::{Mint, Transaction, Transfer};
1717
use num_traits::cast::ToPrimitive;
1818
use proptest::test_runner::{Config as TestRunnerConfig, TestRunner};
@@ -115,6 +115,34 @@ fn account(owner: u64, subaccount: u128) -> Account {
115115
}
116116
}
117117

118+
fn status(env: &StateMachine, index_id: CanisterId) -> Status {
119+
let res = env
120+
.query(index_id, "status", Encode!(&()).unwrap())
121+
.expect("Failed to send status")
122+
.bytes();
123+
Decode!(&res, Status).expect("Failed to decode status response")
124+
}
125+
126+
// Helper function that calls tick on env until either
127+
// the index canister has synced all the blocks up to the
128+
// last one in the ledger or enough attempts passed and therefore
129+
// it fails
130+
fn wait_until_sync_is_completed(env: &StateMachine, index_id: CanisterId, ledger_id: CanisterId) {
131+
const MAX_ATTEMPTS: u8 = 100; // no reason for this number
132+
let mut num_blocks_synced = u64::MAX;
133+
let mut chain_length = u64::MAX;
134+
for _i in 0..MAX_ATTEMPTS {
135+
env.advance_time(Duration::from_secs(60));
136+
env.tick();
137+
num_blocks_synced = status(env, index_id).num_blocks_synced.0.to_u64().unwrap();
138+
chain_length = icrc1_get_blocks(env, ledger_id, 0, 1).chain_length;
139+
if num_blocks_synced == chain_length {
140+
return;
141+
}
142+
}
143+
panic!("The index canister was unable to sync all the blocks with the ledger. Number of blocks synced {} but the Ledger chain length is {}", num_blocks_synced, chain_length);
144+
}
145+
118146
fn icrc1_balance_of(env: &StateMachine, canister_id: CanisterId, account: Account) -> u64 {
119147
let res = env
120148
.execute_ingress(canister_id, "icrc1_balance_of", Encode!(&account).unwrap())
@@ -127,10 +155,15 @@ fn icrc1_balance_of(env: &StateMachine, canister_id: CanisterId, account: Accoun
127155
.expect("Balance must be a u64!")
128156
}
129157

130-
fn icrc1_get_blocks(env: &StateMachine, ledger_id: CanisterId) -> Vec<GenericBlock> {
158+
fn icrc1_get_blocks(
159+
env: &StateMachine,
160+
ledger_id: CanisterId,
161+
start: u64,
162+
length: u64,
163+
) -> icrc_ledger_types::icrc3::blocks::GetBlocksResponse {
131164
let req = GetBlocksRequest {
132-
start: 0.into(),
133-
length: u64::MAX.into(),
165+
start: start.into(),
166+
length: length.into(),
134167
};
135168
let req = Encode!(&req).expect("Failed to encode GetBlocksRequest");
136169
let res = env
@@ -140,15 +173,15 @@ fn icrc1_get_blocks(env: &StateMachine, ledger_id: CanisterId) -> Vec<GenericBlo
140173
let res = Decode!(&res, icrc_ledger_types::icrc3::blocks::GetBlocksResponse)
141174
.expect("Failed to decode GetBlocksResponse");
142175
let mut blocks = vec![];
143-
for archived in res.archived_blocks {
176+
for archived in &res.archived_blocks {
144177
let req = GetBlocksRequest {
145-
start: archived.start,
146-
length: archived.length,
178+
start: archived.start.clone(),
179+
length: archived.length.clone(),
147180
};
148181
let req = Encode!(&req).expect("Failed to encode GetBlocksRequest for archive node");
149182
let canister_id = archived.callback.canister_id.as_ref().try_into().unwrap();
150183
let res = env
151-
.execute_ingress(canister_id, archived.callback.method, req)
184+
.execute_ingress(canister_id, archived.callback.method.clone(), req)
152185
.expect("Failed to send get_blocks request to archive")
153186
.bytes();
154187
let res = Decode!(&res, BlockRange)
@@ -157,7 +190,7 @@ fn icrc1_get_blocks(env: &StateMachine, ledger_id: CanisterId) -> Vec<GenericBlo
157190
blocks.extend(res);
158191
}
159192
blocks.extend(res.blocks);
160-
blocks
193+
icrc_ledger_types::icrc3::blocks::GetBlocksResponse { blocks, ..res }
161194
}
162195

163196
fn get_blocks(env: &StateMachine, index_id: CanisterId) -> GetBlocksResponse {
@@ -256,14 +289,9 @@ fn list_subaccounts(
256289

257290
// Assert that the index canister contains the same blocks as the ledger
258291
fn assert_ledger_index_parity(env: &StateMachine, ledger_id: CanisterId, index_id: CanisterId) {
259-
let ledger_blocks = icrc1_get_blocks(env, ledger_id);
292+
let ledger_blocks = icrc1_get_blocks(env, ledger_id, 0, u64::MAX);
260293
let index_blocks = get_blocks(env, index_id);
261-
assert_eq!(ledger_blocks, index_blocks.blocks);
262-
}
263-
264-
fn trigger_heartbeat(env: &StateMachine) {
265-
env.advance_time(Duration::from_secs(60));
266-
env.tick();
294+
assert_eq!(ledger_blocks.blocks, index_blocks.blocks);
267295
}
268296

269297
#[test]
@@ -276,12 +304,12 @@ fn test_ledger_growing() {
276304
let index_id = install_index(env, ledger_id);
277305

278306
// test initial mint block
279-
trigger_heartbeat(env);
307+
wait_until_sync_is_completed(env, index_id, ledger_id);
280308
assert_ledger_index_parity(env, ledger_id, index_id);
281309

282310
// test first transfer block
283311
transfer(env, ledger_id, account(1, 0), account(2, 0), 1);
284-
trigger_heartbeat(env);
312+
wait_until_sync_is_completed(env, index_id, ledger_id);
285313
assert_ledger_index_parity(env, ledger_id, index_id);
286314

287315
// test multiple blocks
@@ -292,14 +320,14 @@ fn test_ledger_growing() {
292320
] {
293321
transfer(env, ledger_id, from, to, amount);
294322
}
295-
trigger_heartbeat(env);
323+
wait_until_sync_is_completed(env, index_id, ledger_id);
296324
assert_ledger_index_parity(env, ledger_id, index_id);
297325

298326
// test archived blocks
299327
for _i in 0..(ARCHIVE_TRIGGER_THRESHOLD as usize + 1) {
300328
transfer(env, ledger_id, account(1, 0), account(1, 2), 1);
301329
}
302-
trigger_heartbeat(env);
330+
wait_until_sync_is_completed(env, index_id, ledger_id);
303331
assert_ledger_index_parity(env, ledger_id, index_id);
304332
}
305333

@@ -316,7 +344,7 @@ fn test_archive_indexing() {
316344
let ledger_id = install_ledger(env, initial_balances, default_archive_options());
317345
let index_id = install_index(env, ledger_id);
318346

319-
trigger_heartbeat(env);
347+
wait_until_sync_is_completed(env, index_id, ledger_id);
320348
assert_ledger_index_parity(env, ledger_id, index_id);
321349
}
322350

@@ -431,7 +459,7 @@ fn test_get_account_transactions() {
431459

432460
////////////
433461
//// phase 1: only 1 mint to (1, 0)
434-
trigger_heartbeat(env);
462+
wait_until_sync_is_completed(env, index_id, ledger_id);
435463

436464
// account (1, 0) has one mint
437465
let actual_txs =
@@ -446,7 +474,7 @@ fn test_get_account_transactions() {
446474
/////////////
447475
//// phase 2: transfer from (1, 0) to (2, 0)
448476
transfer(env, ledger_id, account(1, 0), account(2, 0), 1_000_000);
449-
trigger_heartbeat(env);
477+
wait_until_sync_is_completed(env, index_id, ledger_id);
450478

451479
// account (1, 0) has one transfer and one mint
452480
let actual_txs =
@@ -470,7 +498,7 @@ fn test_get_account_transactions() {
470498
//// transfer from (2, 0) to (1, 1)
471499
transfer(env, ledger_id, account(1, 0), account(2, 0), 2_000_000);
472500
transfer(env, ledger_id, account(2, 0), account(1, 1), 1_000_000);
473-
trigger_heartbeat(env);
501+
wait_until_sync_is_completed(env, index_id, ledger_id);
474502

475503
// account (1, 0) has two transfers and one mint
476504
let actual_txs =
@@ -511,7 +539,7 @@ fn test_get_account_transactions_start_length() {
511539
})
512540
.collect();
513541

514-
trigger_heartbeat(env);
542+
wait_until_sync_is_completed(env, index_id, ledger_id);
515543

516544
// get the most n recent transaction with start set to none
517545
for n in 1..10 {
@@ -548,7 +576,7 @@ fn test_get_account_transactions_pagination() {
548576
let ledger_id = install_ledger(env, initial_balances, default_archive_options());
549577
let index_id = install_index(env, ledger_id);
550578

551-
trigger_heartbeat(env);
579+
wait_until_sync_is_completed(env, index_id, ledger_id);
552580

553581
// The index get_account_transactions endpoint returns batches of transactions
554582
// in descending order of index, i.e. the first index returned in the result
@@ -625,7 +653,7 @@ fn test_icrc1_balance_of() {
625653
{
626654
icrc1_transfer(env, ledger_id, PrincipalId(*caller), transfer_arg.clone());
627655
}
628-
trigger_heartbeat(env);
656+
wait_until_sync_is_completed(env, index_id, ledger_id);
629657

630658
for account in transactions
631659
.iter()
@@ -680,7 +708,7 @@ fn test_list_subaccounts() {
680708
let ledger_id = install_ledger(env, initial_balances, default_archive_options());
681709
let index_id = install_index(env, ledger_id);
682710

683-
trigger_heartbeat(env);
711+
wait_until_sync_is_completed(env, index_id, ledger_id);
684712

685713
// list account_1.owner subaccounts when no starting subaccount is specified
686714
assert_eq!(

0 commit comments

Comments
 (0)