Skip to content

Commit

Permalink
chore(ICP-Rosetta): FI-1473: remove block test (#1413)
Browse files Browse the repository at this point in the history
This MR introduces the following changes: 

1. Remove the block test from basic tests
2. Remove the account balance test from basic tests (Covered by this MR:
#1296)
3. Add tests for fetching blocks
4. Add tests for search transactions by index

---------

Co-authored-by: Mathias Björkqvist <[email protected]>
  • Loading branch information
NikolasHai and mbjorkqvist authored Sep 17, 2024
1 parent 4dee90b commit 775b650
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 270 deletions.
18 changes: 18 additions & 0 deletions rs/rosetta-api/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use anyhow::Context;
use candid::Nat;
use candid::Principal;
use ic_rosetta_api::convert::to_model_account_identifier;
use ic_rosetta_api::models::BlockIdentifier;
use ic_rosetta_api::models::ConstructionMetadataRequestOptions;
use ic_rosetta_api::models::ConstructionPayloadsRequestMetadata;
use ic_rosetta_api::models::OperationIdentifier;
Expand Down Expand Up @@ -266,6 +267,23 @@ impl RosettaClient {
.await
}

pub async fn block_transaction(
&self,
network_identifier: NetworkIdentifier,
transaction_identifier: TransactionIdentifier,
block_identifier: BlockIdentifier,
) -> anyhow::Result<BlockTransactionResponse> {
self.call_endpoint(
"/block/transaction",
&BlockTransactionRequest {
network_identifier,
transaction_identifier,
block_identifier,
},
)
.await
}

pub fn sign_transaction<T>(
signer_keypair: &T,
payloads: ConstructionPayloadsResponse,
Expand Down
260 changes: 3 additions & 257 deletions rs/rosetta-api/tests/basic_tests.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
use crate::test_utils::{get_balance, TestLedger};
use crate::test_utils::TestLedger;
use ic_ledger_canister_blocks_synchronizer::blocks::Blocks;
use ic_ledger_canister_blocks_synchronizer::blocks::HashedBlock;
use ic_ledger_canister_blocks_synchronizer_test_utils::create_tmp_dir;
use ic_ledger_canister_blocks_synchronizer_test_utils::sample_data::{acc_id, Scribe};
use ic_ledger_canister_blocks_synchronizer_test_utils::sample_data::Scribe;
use ic_ledger_core::block::BlockType;
use ic_ledger_core::tokens::CheckedAdd;
use ic_rosetta_api::convert::{from_hash, to_hash};
use ic_rosetta_api::errors::ApiError;
use ic_rosetta_api::models::CallRequest;
use ic_rosetta_api::models::PartialBlockIdentifier;
use ic_rosetta_api::models::QueryBlockRangeRequest;
use ic_rosetta_api::models::QueryBlockRangeResponse;
use ic_rosetta_api::models::{
BlockIdentifier, BlockRequest, BlockTransaction, BlockTransactionRequest,
SearchTransactionsRequest, SearchTransactionsResponse,
};
use ic_rosetta_api::models::{SearchTransactionsRequest, SearchTransactionsResponse};
use ic_rosetta_api::request_handler::RosettaRequestHandler;
use ic_rosetta_api::MAX_BLOCKS_PER_QUERY_BLOCK_RANGE_REQUEST;
use icp_ledger::{self, AccountIdentifier, Block, BlockIndex, Tokens};
Expand All @@ -24,255 +19,6 @@ use std::sync::Arc;

mod test_utils;

#[actix_rt::test]
async fn blocks_test() {
let ledger = Arc::new(TestLedger::new());
let req_handler = RosettaRequestHandler::new_with_default_blockchain(ledger.clone());
let mut scribe = Scribe::new();
let num_transactions: usize = 100;
let num_accounts = 10;

scribe.gen_accounts(num_accounts, 1_000_000);
for _i in 0..num_transactions {
scribe.gen_transaction();
}

for b in &scribe.blockchain {
ledger.add_block(b.clone()).await.ok();
}

let h = num_accounts as usize + 17;
for i in 0..num_accounts {
let acc = acc_id(i);
assert_eq!(
get_balance(&req_handler, Some(h), acc).await.unwrap(),
*scribe.balance_history[h].get(&acc).unwrap()
);
}

// fetch by index
let block_id = PartialBlockIdentifier {
index: Some(h.try_into().unwrap()),
hash: None,
};
let msg = BlockRequest::new(req_handler.network_id(), block_id);
let resp = req_handler.block(msg).await.unwrap();

let block = resp.block.unwrap();
assert_eq!(
to_hash(&block.block_identifier.hash).unwrap(),
scribe.blockchain[h].hash
);

// fetch by hash
let block_id = PartialBlockIdentifier {
index: None,
hash: Some(from_hash(&scribe.blockchain[h].hash)),
};
let msg = BlockRequest::new(req_handler.network_id(), block_id);
let resp = req_handler.block(msg).await.unwrap();
let block = resp.block.unwrap();

assert_eq!(block.block_identifier.index as usize, h);
assert_eq!(block.parent_block_identifier.index as usize, h - 1);
assert_eq!(
to_hash(&block.parent_block_identifier.hash).unwrap(),
scribe.blockchain[h - 1].hash
);

// now fetch a transaction
let trans = block.transactions[0].clone();

let block_id = BlockIdentifier {
index: h.try_into().unwrap(),
hash: from_hash(&scribe.blockchain[h].hash),
};
let msg = BlockTransactionRequest::new(
req_handler.network_id(),
block_id.clone(),
trans.transaction_identifier.clone(),
);
let resp = req_handler.block_transaction(msg).await.unwrap();

assert_eq!(
trans.transaction_identifier.hash,
resp.transaction.transaction_identifier.hash
);

let resp = req_handler
.search_transactions(
SearchTransactionsRequest::builder(req_handler.network_id())
.with_transaction_identifier(trans.transaction_identifier.clone())
.build(),
)
.await
.unwrap();
assert_eq!(
resp.transactions,
vec![BlockTransaction {
block_identifier: block_id,
transaction: trans
}]
);
assert_eq!(resp.total_count, 1);

let resp = req_handler
.search_transactions(SearchTransactionsRequest::builder(req_handler.network_id()).build())
.await
.unwrap();

assert_eq!(resp.total_count, scribe.blockchain.len() as i64);
assert_eq!(resp.transactions.len(), scribe.blockchain.len());
assert_eq!(resp.next_offset, None);

let mut req = SearchTransactionsRequest::builder(req_handler.network_id()).build();
req.max_block = Some(100);
req.limit = Some(10);
req.offset = Some(30);

let resp = req_handler.search_transactions(req.clone()).await.unwrap();

assert_eq!(resp.total_count, 71);
assert_eq!(resp.transactions.len(), 10);
assert_eq!(
resp.transactions.first().unwrap().block_identifier.index,
70
);
assert_eq!(resp.transactions.last().unwrap().block_identifier.index, 61);
assert_eq!(resp.next_offset, Some(40));

req.offset = Some(40);

let resp = req_handler.search_transactions(req.clone()).await.unwrap();

assert_eq!(resp.total_count, 61);
assert_eq!(resp.transactions.len(), 10);
assert_eq!(
resp.transactions.first().unwrap().block_identifier.index,
60
);
assert_eq!(resp.transactions.last().unwrap().block_identifier.index, 51);
assert_eq!(resp.next_offset, Some(50));

for block in scribe.blockchain.clone() {
let partial_block_id = PartialBlockIdentifier {
index: Some(block.index),
hash: None,
};
let msg = BlockRequest::new(req_handler.network_id(), partial_block_id);
let resp = req_handler.block(msg).await.unwrap();
let transactions = vec![
ic_rosetta_api::convert::hashed_block_to_rosetta_core_transaction(
&block,
ic_rosetta_api::DEFAULT_TOKEN_SYMBOL,
)
.unwrap(),
];
assert_eq!(resp.clone().block.unwrap().transactions, transactions);
let transaction = resp.block.unwrap().transactions[0].clone();
let block_id = BlockIdentifier {
index: block.index,
hash: from_hash(&block.hash),
};
let msg = BlockTransactionRequest::new(
req_handler.network_id(),
block_id.clone(),
transaction.transaction_identifier.clone(),
);
let resp = req_handler.block_transaction(msg).await.unwrap();
assert_eq!(resp.transaction, transactions[0]);
let resp = req_handler
.search_transactions(
SearchTransactionsRequest::builder(req_handler.network_id())
.with_transaction_identifier(transaction.transaction_identifier)
.build(),
)
.await
.unwrap();
assert_eq!(
resp.transactions
.into_iter()
.map(|t| t.transaction)
.collect::<Vec<ic_rosetta_api::models::Transaction>>()[0],
transactions[0]
);
}
}

#[actix_rt::test]
async fn balances_test() {
let ledger = Arc::new(TestLedger::new());
let req_handler = RosettaRequestHandler::new_with_default_blockchain(ledger.clone());
let mut scribe = Scribe::new();

scribe.gen_accounts(2, 1_000_000);
for b in &scribe.blockchain {
ledger.add_block(b.clone()).await.unwrap();
}

let acc0 = acc_id(0);
let acc1 = acc_id(1);

assert_eq!(
get_balance(&req_handler, None, acc0).await.unwrap(),
*scribe.balance_book.get(&acc0).unwrap()
);
assert_eq!(
get_balance(&req_handler, None, acc1).await.unwrap(),
*scribe.balance_book.get(&acc1).unwrap()
);

scribe.buy(acc0, 10);
ledger
.add_block(scribe.blockchain.back().unwrap().clone())
.await
.unwrap();
assert_eq!(
get_balance(&req_handler, None, acc0).await.unwrap(),
*scribe.balance_book.get(&acc0).unwrap()
);
assert_eq!(
get_balance(&req_handler, None, acc1).await.unwrap(),
*scribe.balance_book.get(&acc1).unwrap()
);

let after_buy_balance = *scribe.balance_book.get(&acc0).unwrap();

scribe.sell(acc0, 100);
ledger
.add_block(scribe.blockchain.back().unwrap().clone())
.await
.unwrap();
assert_eq!(
get_balance(&req_handler, None, acc0).await.unwrap(),
*scribe.balance_book.get(&acc0).unwrap()
);
assert_eq!(
get_balance(&req_handler, None, acc1).await.unwrap(),
*scribe.balance_book.get(&acc1).unwrap()
);

scribe.transfer(acc0, acc1, 1000);
ledger
.add_block(scribe.blockchain.back().unwrap().clone())
.await
.unwrap();
assert_eq!(
get_balance(&req_handler, None, acc0).await.unwrap(),
*scribe.balance_book.get(&acc0).unwrap()
);
assert_eq!(
get_balance(&req_handler, None, acc1).await.unwrap(),
*scribe.balance_book.get(&acc1).unwrap()
);

// and test if we can access arbitrary block
assert_eq!(
get_balance(&req_handler, Some(2), acc0).await.unwrap(),
after_buy_balance
);
}

fn verify_balances(scribe: &Scribe, blocks: &Blocks, start_idx: usize) {
for hb in scribe.blockchain.iter().skip(start_idx) {
assert_eq!(*hb, blocks.get_hashed_block(&hb.index).unwrap());
Expand Down
19 changes: 7 additions & 12 deletions rs/rosetta-api/tests/system_tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub async fn assert_rosetta_blockchain_is_valid(
.network_status(network_identifier.clone())
.await
.unwrap();
let ledger_tip = query_encoded_blocks(agent, None, 1).await.blocks[0].clone();
let ledger_tip = query_encoded_blocks(agent, u64::MAX, u64::MAX).await.blocks[0].clone();
assert_eq!(
to_hash(&network_status.current_block_identifier.hash).unwrap(),
icp_ledger::Block::block_hash(&ledger_tip),
Expand All @@ -104,13 +104,13 @@ pub fn memo_bytebuf_to_u64(bytebuf: &[u8]) -> Option<u64> {
Some(value)
}

/// This function calls the `query_encoded_blocks` endpoint on the ledger canister.
/// The user can specify the maximum block height and the number of blocks to query.
/// If the maximum block height is not specified then the current chain tip index will be used.
/// This function calls the 'query_encoded_blocks' endpoint on the ledger canister.
/// The user can specify the minimum block height and the number of blocks to query.
/// If the minimum block height is greater than the current chain tip index, the function will cap the start index to the current chain tip index.
/// If the number of blocks to query is greater than the chain length, the function will cap the length to the chain length.
pub async fn query_encoded_blocks(
agent: &Agent,
// If this is left None then the whatever the currently highest block is will be used.
max_block_height: Option<u64>,
min_block_height: u64,
num_blocks: u64,
) -> QueryEncodedBlocksResponse {
let response = Decode!(
Expand All @@ -132,12 +132,7 @@ pub async fn query_encoded_blocks(

let current_chain_tip_index = response.chain_length.saturating_sub(1);
let block_request = GetBlocksArgs {
// If max_block_height is None then we will use the current chain tip index.
// Otherwise we will use the minimum of the max_block_height and the current chain tip index.
start: std::cmp::min(
max_block_height.unwrap_or(current_chain_tip_index),
current_chain_tip_index,
),
start: std::cmp::min(min_block_height, current_chain_tip_index),
length: std::cmp::min(num_blocks, response.chain_length) as usize,
};
Decode!(
Expand Down
Loading

0 comments on commit 775b650

Please sign in to comment.