Skip to content

Commit

Permalink
add: test for deduplication
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolasHaimerl committed Aug 11, 2023
2 parents 1f6bd83 + 2fd2649 commit 6540f1e
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 13 deletions.
2 changes: 2 additions & 0 deletions cycles-ledger/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub type NumCycles = Nat;
pub struct DepositArg {
pub to: Account,
pub memo: Option<Memo>,
#[serde(default)]
pub created_at_time: Option<u64>,
}

#[derive(CandidType, Deserialize, Clone, Debug, PartialEq, Eq)]
Expand Down
14 changes: 11 additions & 3 deletions cycles-ledger/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,13 @@ fn deposit(arg: endpoints::DepositArg) -> endpoints::DepositResult {
ic_cdk::trap("deposit amount is insufficient");
}
let memo = validate_memo(arg.memo);
let (txid, balance, _phash) =
storage::record_deposit(&arg.to, amount, memo, ic_cdk::api::time());
let (txid, balance, _phash) = storage::record_deposit(
&arg.to,
amount,
memo,
ic_cdk::api::time(),
arg.created_at_time,
);

// TODO(FI-766): set the certified variable.

Expand Down Expand Up @@ -178,6 +183,7 @@ fn icrc1_transfer(args: TransferArg) -> Result<Nat, TransferError> {
amount,
fee: config::FEE,
memo,
created_at_time: args.created_at_time
};

deduplicate(args.created_at_time, operation.hash(), now)?;
Expand Down Expand Up @@ -274,6 +280,7 @@ async fn send(args: endpoints::SendArg) -> Result<Nat, SendError> {
amount,
fee: config::FEE,
memo: memo.clone(),
created_at_time: args.created_at_time
};
let now = ic_cdk::api::time();
deduplicate(args.created_at_time, operation.hash(), now)
Expand Down Expand Up @@ -302,7 +309,8 @@ async fn send(args: endpoints::SendArg) -> Result<Nat, SendError> {
},
))
} else {
let (send, _send_hash) = storage::send(&from, amount, memo, now);
let now = ic_cdk::api::time();
let (send, _send_hash) = storage::send(&from, amount, memo, now, args.created_at_time);
Ok(send)
}
}
Expand Down
26 changes: 25 additions & 1 deletion cycles-ledger/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub enum Operation {
fee: u128,
#[serde(skip_serializing_if = "Option::is_none")]
memo: Option<Memo>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "ts")]
created_at_time: Option<u64>,
},
Transfer {
#[serde(with = "compact_account")]
Expand All @@ -54,6 +58,10 @@ pub enum Operation {
fee: u128,
#[serde(skip_serializing_if = "Option::is_none")]
memo: Option<Memo>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "ts")]
created_at_time: Option<u64>,
},
Burn {
#[serde(with = "compact_account")]
Expand All @@ -63,6 +71,10 @@ pub enum Operation {
fee: u128,
#[serde(skip_serializing_if = "Option::is_none")]
memo: Option<Memo>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "ts")]
created_at_time: Option<u64>,
},
}

Expand Down Expand Up @@ -221,6 +233,7 @@ pub fn record_deposit(
amount: u128,
memo: Option<Memo>,
now: u64,
created_at_time: Option<u64>,
) -> (u64, u128, Hash) {
assert!(amount >= crate::config::FEE);

Expand All @@ -236,6 +249,7 @@ pub fn record_deposit(
amount,
memo,
fee: crate::config::FEE,
created_at_time,
},
timestamp: now,
phash,
Expand Down Expand Up @@ -293,6 +307,7 @@ pub fn transfer(operation: Operation, now: u64) -> (u64, Hash) {
amount,
fee,
memo: _,
created_at_time:_
} => {
let from_key = to_account_key(&from);
let to_key = to_account_key(&to);
Expand Down Expand Up @@ -349,6 +364,7 @@ pub fn penalize(from: &Account, now: u64) -> (BlockIndex, Hash) {
amount: 0,
memo: None,
fee: crate::config::FEE,
created_at_time: None,
},
timestamp: now,
phash,
Expand All @@ -357,7 +373,13 @@ pub fn penalize(from: &Account, now: u64) -> (BlockIndex, Hash) {
})
}

pub fn send(from: &Account, amount: u128, memo: Option<Memo>, now: u64) -> (BlockIndex, Hash) {
pub fn send(
from: &Account,
amount: u128,
memo: Option<Memo>,
now: u64,
created_at_time: Option<u64>,
) -> (BlockIndex, Hash) {
let from_key = to_account_key(from);

mutate_state(|s| {
Expand All @@ -379,6 +401,7 @@ pub fn send(from: &Account, amount: u128, memo: Option<Memo>, now: u64) -> (Bloc
amount,
memo,
fee: crate::config::FEE,
created_at_time,
},
timestamp: now,
phash,
Expand Down Expand Up @@ -411,6 +434,7 @@ mod tests {
amount: u128::MAX,
fee: 10_000,
memo: Some(Memo::default()),
created_at_time: None,
},
timestamp: 1691065957,
phash: None,
Expand Down
76 changes: 67 additions & 9 deletions cycles-ledger/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ fn test_send_flow() {

// send cycles from subaccount with created_at_time set
let now = env
.time()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
.time()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
let send_receiver_balance = env.cycle_balance(send_receiver);
let send_amount = 300_000_000_u128;
let _send_idx = send(
Expand Down Expand Up @@ -486,7 +486,7 @@ fn test_transfer() {
let fee = fee(env, ledger_id);

let transfer_amount = Nat::from(100_000);
let idx = transfer(
transfer(
env,
ledger_id,
user1,
Expand Down Expand Up @@ -550,7 +550,7 @@ fn test_transfer() {
);

// Should not be able commit a transaction that was created in the future
let now = env
let mut now = env
.time()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
Expand All @@ -573,9 +573,66 @@ fn test_transfer() {
.unwrap_err()
);

// When making the same transaction again
// Should be able to make a transfer when created time is valid
let tx: Nat= transfer(
env,
ledger_id,
user1,
TransferArg {
from_subaccount: None,
to: user2,
fee: None,
created_at_time: Some(now),
memo: None,
amount: transfer_amount.clone(),
},
)
.unwrap();

// Should not be able send the same transfer twice if created_at_time is set
assert_eq!(
TransferError::Duplicate { duplicate_of: idx },
TransferError::Duplicate { duplicate_of: tx},
transfer(
env,
ledger_id,
user1,
TransferArg {
from_subaccount: None,
to: user2,
fee: None,
created_at_time: Some(now),
memo: None,
amount: transfer_amount.clone(),
},
)
.unwrap_err()
);

// Setting a different memo field should result in no deduplication
transfer(
env,
ledger_id,
user1,
TransferArg {
from_subaccount: None,
to: user2,
fee: None,
created_at_time: Some(now),
memo: Some(Memo(ByteBuf::from(b"1234".to_vec()))),
amount: transfer_amount.clone(),
},
)
.unwrap();

// Advance time so that the deduplication window is over
env.advance_time(config::TRANSACTION_WINDOW*2);
now = env
.time()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;

// Now the transfer which was deduplicated previously should be ok
transfer(
env,
ledger_id,
Expand All @@ -589,5 +646,6 @@ fn test_transfer() {
amount: transfer_amount.clone(),
},
)
.unwrap_err());
.unwrap();

}
1 change: 1 addition & 0 deletions depositor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async fn deposit(arg: DepositArg) -> DepositResult {
let arg = cycles_ledger::endpoints::DepositArg {
to: arg.to,
memo: arg.memo,
created_at_time: Some(ic_cdk::api::time()),
};
let (result,): (DepositResult,) = call_with_payment128(ledger_id, "deposit", (arg,), cycles)
.await
Expand Down

0 comments on commit 6540f1e

Please sign in to comment.