Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(ICRC_ledger): FI-1397: Add transaction generation to ICRC golden state tests #1478

Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions rs/rosetta-api/icrc1/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,17 @@ rust_test(
# bazel \
# test \
# --test_env=SSH_AUTH_SOCK \
# --test_timeout=43200 \
# //rs/rosetta-api/icrc1:icrc_ledger_suite_integration_golden_state_upgrade_downgrade_test
#
# To run the U256 token version of the test (for ckETH and ckERC20 tokens), use:
#
# //rs/rosetta-api/icrc1:icrc_ledger_suite_integration_golden_state_upgrade_downgrade_test_u256
#
# The only unusual thing in this command is `--test_env=SSH_AUTH_SOCK`. That causes the
# SSH_AUTH_SOCK environment variable to be "forwarded" from your shell to the sandbox where the test
# is run. This authorizes the test to download the test data.
# The unusual things in this command are:
# - `--test_env=SSH_AUTH_SOCK`: This causes the SSH_AUTH_SOCK environment variable to be "forwarded" from
# your shell to the sandbox where the test is run. This authorizes the test to download the test data.
# - `--test_timeout=43200`: This sets the test timeout to 12 hours (more than currently required).
#
# Additionally, the following flags are recommended (but not required):
#
Expand Down Expand Up @@ -141,6 +143,9 @@ rust_test(
"//rs/rosetta-api/icrc1/ledger",
"//rs/rosetta-api/icrc1/ledger/sm-tests:sm-tests" + name_suffix,
"//rs/rosetta-api/icrc1/test_utils",
"//rs/rosetta-api/icrc1/tokens_u256",
"//rs/rosetta-api/icrc1/tokens_u64",
"//rs/rosetta-api/ledger_core",
"//rs/rust_canisters/canister_test",
"//rs/state_machine_tests",
"//rs/test_utilities/load_wasm",
Expand Down
62 changes: 39 additions & 23 deletions rs/rosetta-api/icrc1/ledger/sm-tests/src/in_memory_ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@ impl From<ApprovalKey> for (Account, Account) {
}
}

trait InMemoryLedgerState {
pub trait InMemoryLedgerState {
type AccountId;
type Tokens;

fn get_allowance_if_set(
mbjorkqvist marked this conversation as resolved.
Show resolved Hide resolved
&self,
from: &Self::AccountId,
spender: &Self::AccountId,
) -> Option<Allowance<Self::Tokens>>;

fn process_approve(
&mut self,
from: &Self::AccountId,
Expand Down Expand Up @@ -85,6 +91,15 @@ where
type AccountId = AccountId;
type Tokens = Tokens;

fn get_allowance_if_set(
&self,
from: &Self::AccountId,
spender: &Self::AccountId,
) -> Option<Allowance<Self::Tokens>> {
let key = K::from((from, spender));
self.allowances.get(&key).cloned()
}

fn process_approve(
&mut self,
from: &Self::AccountId,
Expand Down Expand Up @@ -257,12 +272,16 @@ where
) {
let key = K::from((from, spender));
if let Some(expected_allowance) = expected_allowance {
let current_allowance = self
let current_allowance_amount = self
.allowances
.get(&key)
.unwrap_or_else(|| panic!("No current allowance but expected allowance set"));
if current_allowance.amount != *expected_allowance {
panic!("Expected allowance does not match current allowance");
.map(|allowance| allowance.amount.clone())
.unwrap_or(Tokens::zero());
if current_allowance_amount != *expected_allowance {
panic!(
"Expected allowance ({:?}) does not match current allowance ({:?})",
expected_allowance, current_allowance_amount
);
}
}
if amount == &Tokens::zero() {
Expand Down Expand Up @@ -336,42 +355,40 @@ where
}

impl InMemoryLedger<ApprovalKey, Account, Tokens> {
fn new_from_icrc1_ledger_blocks(
blocks: &[ic_icrc1::Block<Tokens>],
burns_without_spender: Option<BurnsWithoutSpender<Account>>,
) -> InMemoryLedger<ApprovalKey, Account, Tokens> {
let mut state = InMemoryLedger {
pub fn new(burns_without_spender: Option<BurnsWithoutSpender<Account>>) -> Self {
InMemoryLedger {
burns_without_spender,
..Default::default()
};
}
}

pub fn ingest_icrc1_ledger_blocks(&mut self, blocks: &[ic_icrc1::Block<Tokens>]) {
for (index, block) in blocks.iter().enumerate() {
if let Some(fee_collector) = block.fee_collector {
state.fee_collector = Some(fee_collector);
self.fee_collector = Some(fee_collector);
}
match &block.transaction.operation {
Operation::Mint { to, amount } => state.process_mint(to, amount),
Operation::Mint { to, amount } => self.process_mint(to, amount),
Operation::Transfer {
from,
to,
spender,
amount,
fee,
} => {
state.process_transfer(from, to, spender, amount, &fee.or(block.effective_fee))
}
} => self.process_transfer(from, to, spender, amount, &fee.or(block.effective_fee)),
Operation::Burn {
from,
spender,
amount,
} => state.process_burn(from, spender, amount, index),
} => self.process_burn(from, spender, amount, index),
Operation::Approve {
from,
spender,
amount,
expected_allowance,
expires_at,
fee,
} => state.process_approve(
} => self.process_approve(
from,
spender,
amount,
Expand All @@ -381,12 +398,11 @@ impl InMemoryLedger<ApprovalKey, Account, Tokens> {
TimeStamp::from_nanos_since_unix_epoch(block.timestamp),
),
}
state.validate_invariants();
self.validate_invariants();
}
state.prune_expired_allowances(TimeStamp::from_nanos_since_unix_epoch(
self.prune_expired_allowances(TimeStamp::from_nanos_since_unix_epoch(
blocks.last().unwrap().timestamp,
));
state
}

pub fn apply_arg_with_caller(
Expand Down Expand Up @@ -522,8 +538,8 @@ pub fn verify_ledger_state(
println!("verifying state of ledger {}", ledger_id);
let blocks = get_all_ledger_and_archive_blocks(env, ledger_id);
println!("retrieved all ledger and archive blocks");
let expected_ledger_state =
InMemoryLedger::new_from_icrc1_ledger_blocks(&blocks, burns_without_spender);
let mut expected_ledger_state = InMemoryLedger::new(burns_without_spender);
expected_ledger_state.ingest_icrc1_ledger_blocks(&blocks);
println!("recreated expected ledger state");
expected_ledger_state.verify_balances_and_allowances(env, ledger_id);
println!("ledger state verified successfully");
Expand Down
26 changes: 15 additions & 11 deletions rs/rosetta-api/icrc1/ledger/sm-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ fn model_transfer(
((from_balance, to_balance), None)
}

fn send_transfer(
pub fn send_transfer(
env: &StateMachine,
ledger: CanisterId,
from: Principal,
Expand Down Expand Up @@ -539,6 +539,19 @@ pub fn balance_of(env: &StateMachine, ledger: CanisterId, acc: impl Into<Account
.unwrap()
}

pub fn fee(env: &StateMachine, ledger: CanisterId) -> u64 {
Decode!(
&env.query(ledger, "icrc1_fee", Encode!().unwrap())
.expect("failed to query fee")
.bytes(),
Nat
)
.expect("failed to decode icrc1_fee response")
.0
.to_u64()
.unwrap()
}

pub fn metadata(env: &StateMachine, ledger: CanisterId) -> BTreeMap<String, Value> {
Decode!(
&env.query(ledger, "icrc1_metadata", Encode!().unwrap())
Expand Down Expand Up @@ -1948,16 +1961,7 @@ where
.expect("failed to decode balance_of response");
assert_eq!(token_name_after_upgrade, OTHER_TOKEN_NAME);

let token_fee_after_upgrade: u64 = Decode!(
&env.query(canister_id, "icrc1_fee", Encode!().unwrap())
.expect("failed to query fee")
.bytes(),
Nat
)
.expect("failed to decode balance_of response")
.0
.to_u64()
.unwrap();
let token_fee_after_upgrade = fee(&env, canister_id);
assert_eq!(token_fee_after_upgrade, NEW_FEE);
}

Expand Down
Loading
Loading