Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion packages/confidential/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ pub trait ConfidentialToken {
/// account so its spending key is available for the `dvk` escrow ECDH.
/// * `live_until_ledger` - The ledger number at which the delegation
/// expires. Spending is authorized while `ledger.sequence() <=
/// live_until_ledger`. The escrowed value persists until
/// live_until_ledger`. Must be between the current ledger and
/// `max_live_until_ledger`, inclusive. The escrowed value persists until
/// [`ConfidentialToken::revoke_spender`].
/// * `data` - XDR-encoded [`SetSpenderData`].
///
Expand Down Expand Up @@ -596,6 +597,10 @@ pub enum ConfidentialTokenError {
/// boundary to keep stored state and emitted events byte-unique per
/// logical value.
NonCanonicalEncoding = 3514,
/// Indicates `live_until_ledger` is outside the accepted range: below the
/// current ledger, so the delegation would be expired on creation, or above
/// `max_live_until_ledger`.
InvalidLiveUntilLedger = 3515,
}

// ################## CONSTANTS ##################
Expand Down
12 changes: 12 additions & 0 deletions packages/confidential/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,11 +841,15 @@ pub fn confidential_transfer_from(
/// account so its spending key is available for the `dvk` escrow ECDH.
/// * `live_until_ledger` - The ledger number at which the delegation expires.
/// Spending is authorized while `ledger.sequence() <= live_until_ledger`.
/// Must be between the current ledger and `max_live_until_ledger`, inclusive.
/// * `payload` - The decoded [`SetSpenderPayload`].
/// * `proof` - The raw UltraHonk proof bytes.
///
/// # Errors
///
/// * [`ConfidentialTokenError::InvalidLiveUntilLedger`] - When
/// `live_until_ledger` is lower than the current ledger sequence or greater
/// than `max_live_until_ledger`.
/// * [`ConfidentialTokenError::AccountNotRegistered`] - When `account` or
/// `spender` is not registered.
/// * [`ConfidentialTokenError::DelegationAlreadyExists`] - When a delegation
Expand Down Expand Up @@ -874,6 +878,14 @@ pub fn set_spender(
payload: &SetSpenderPayload,
proof: &Bytes,
) {
// Same bounds as the fungible `set_allowance`, checked before the account
// lookups, the auditor call and proof verification.
if live_until_ledger < e.ledger().sequence()
|| live_until_ledger > e.ledger().max_live_until_ledger()
{
panic_with_error!(e, ConfidentialTokenError::InvalidLiveUntilLedger);
}

let owner = get_account(e, account);
let spender_account = get_account(e, spender);
let auditor = ConfidentialAuditorClient::new(e, &get_auditor(e));
Expand Down
59 changes: 59 additions & 0 deletions packages/confidential/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,65 @@ fn set_spender_twice_panics() {
h.token.set_spender(&alice, &spender, &1_000u32, &set_spender_data(&h.e));
}

#[test]
#[should_panic(expected = "Error(Contract, #3515)")]
fn set_spender_with_past_live_until_ledger_panics() {
let h = setup();
let alice = Address::generate(&h.e);
let spender = Address::generate(&h.e);

h.token.register(&alice, &1u32, &register_data(&h.e));
h.token.register(&spender, &1u32, &register_data(&h.e));

h.e.ledger().set_sequence_number(100);
h.token.set_spender(&alice, &spender, &99u32, &set_spender_data(&h.e));
}

#[test]
#[should_panic(expected = "Error(Contract, #3515)")]
fn set_spender_beyond_max_live_until_ledger_panics() {
let h = setup();
let alice = Address::generate(&h.e);
let spender = Address::generate(&h.e);

h.token.register(&alice, &1u32, &register_data(&h.e));
h.token.register(&spender, &1u32, &register_data(&h.e));

let max = h.e.ledger().max_live_until_ledger();
h.token.set_spender(&alice, &spender, &(max + 1), &set_spender_data(&h.e));
}

#[test]
fn set_spender_with_max_live_until_ledger_succeeds() {
let h = setup();
let alice = Address::generate(&h.e);
let spender = Address::generate(&h.e);

h.token.register(&alice, &1u32, &register_data(&h.e));
h.token.register(&spender, &1u32, &register_data(&h.e));

let max = h.e.ledger().max_live_until_ledger();
h.token.set_spender(&alice, &spender, &max, &set_spender_data(&h.e));

assert_eq!(h.token.get_spender_delegation(&alice, &spender).live_until_ledger, max);
}

#[test]
fn set_spender_with_current_ledger_as_live_until_ledger_succeeds() {
let h = setup();
let alice = Address::generate(&h.e);
let spender = Address::generate(&h.e);

h.token.register(&alice, &1u32, &register_data(&h.e));
h.token.register(&spender, &1u32, &register_data(&h.e));

h.e.ledger().set_sequence_number(100);
h.token.set_spender(&alice, &spender, &100u32, &set_spender_data(&h.e));

assert_eq!(h.token.get_spender_delegation(&alice, &spender).live_until_ledger, 100);
assert!(h.token.is_spender(&alice, &spender));
}

#[test]
fn revoke_spender_deletes_delegation() {
let h = setup();
Expand Down
Loading