Skip to content

Commit

Permalink
[ABW-3915] Expiration errors (#258)
Browse files Browse the repository at this point in the history
* get expiration status

* add new Error types

* wip

* updates to use u64 instead of Timestamp

* bump Sargon

* lint

* Add Siwft Tests

* Add extension for status

---------

Co-authored-by: micbakos-rdx <[email protected]>
  • Loading branch information
matiasbzurovski and micbakos-rdx authored Nov 8, 2024
1 parent 82af570 commit 456272e
Show file tree
Hide file tree
Showing 23 changed files with 322 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation
import SargonUniFFI

extension DappToWalletInteractionSubintentExpiration {
public func getStatus() -> DappToWalletInteractionSubintentExpirationStatus {
getSubintentExpirationStatus(expiration: self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation
import SargonUniFFI

extension DappToWalletInteractionSubintentExpireAtTime {
public var date: Date {
.init(timeIntervalSince1970: TimeInterval(unixTimestampSeconds))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation
import SargonUniFFI
import XCTest

final class DappToWalletInteractionSubintentExpirationTests: TestCase {
typealias SUT = DappToWalletInteractionSubintentExpiration

func testGetStatus() throws {
let afterDelay = SUT.afterDelay(.init(expireAfterSeconds: 100))
XCTAssertEqual(afterDelay.getStatus(), .valid)

let atTimeExpired = SUT.atTime(.init(unixTimestampSeconds: 0))
XCTAssertEqual(atTimeExpired.getStatus(), .expired)

let now = UInt64(Date.now.timeIntervalSince1970)
let atTimeCloseToExpiration = SUT.atTime(.init(unixTimestampSeconds: now + 15))
XCTAssertEqual(atTimeCloseToExpiration.getStatus(), .expirationTooClose)

let atTimeValid = SUT.atTime(.init(unixTimestampSeconds: now + 60))
XCTAssertEqual(atTimeValid.getStatus(), .valid)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation
import SargonUniFFI
import XCTest

final class DappToWalletInteractionSubintentExpireAtTimeTests: TestCase {
typealias SUT = DappToWalletInteractionSubintentExpireAtTime

func testDate() throws {
var sut = SUT.init(unixTimestampSeconds: 0)
XCTAssertEqual(sut.date, Date(timeIntervalSince1970: 0))

sut = .init(unixTimestampSeconds: 500)
XCTAssertEqual(sut.date, Date(timeIntervalSince1970: 500))

sut = .init(unixTimestampSeconds: 1000)
XCTAssertEqual(sut.date.timeIntervalSince1970, 1000)
}
}
2 changes: 1 addition & 1 deletion crates/sargon-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sargon-uniffi"
# Don't forget to update version in crates/sargon/Cargo.toml
version = "1.1.47"
version = "1.1.48"
edition = "2021"
build = "build.rs"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ use sargon::DappToWalletInteractionSubintentExpireAtTime as InternalDappToWallet
#[derive(Clone, PartialEq, InternalConversion, uniffi::Record)]
pub struct DappToWalletInteractionSubintentExpireAtTime {
/// The unix timestamp in seconds when the subintent expires.
pub unix_timestamp_seconds: Timestamp,
pub unix_timestamp_seconds: u64,
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,10 @@ pub enum DappToWalletInteractionSubintentExpiration {
/// 100 XRD over 2 USDT will succeed. Otherwise, it would fail.
AfterDelay(DappToWalletInteractionSubintentExpireAfterDelay),
}

#[uniffi::export]
pub fn get_subintent_expiration_status(
expiration: &DappToWalletInteractionSubintentExpiration,
) -> DappToWalletInteractionSubintentExpirationStatus {
expiration.into_internal().get_status().into()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod after_delay;
mod at_time;
mod expiration;
mod status;

pub use after_delay::*;
pub use at_time::*;
pub use expiration::*;
pub use status::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::prelude::*;
use sargon::DappToWalletInteractionSubintentExpirationStatus as InternalDappToWalletInteractionSubintentExpirationStatus;

/// An enum that represents the expiration status of a subintent at a given time.
///
/// Useful for determining if a subintent is still valid at the moment the Host apps
/// receive the corresponding request.
#[derive(Clone, PartialEq, InternalConversion, uniffi::Enum)]
pub enum DappToWalletInteractionSubintentExpirationStatus {
/// The subintent hasn't expired yet
Valid,

/// The subintent is too close to its expiration. Although it hasn't expired yet, the Host apps
/// shouldn't allow the user dealing with it.
ExpirationTooClose,

/// The subintent has already expired.
Expired,
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ pub enum DappWalletInteractionErrorType {
IncompatibleVersion,
FailedToSignAuthChallenge,
InvalidPersonaOrAccounts,
ExpiredSubintent,
SubintentExpirationTooClose,
}
2 changes: 1 addition & 1 deletion crates/sargon/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sargon"
# Don't forget to update version in crates/sargon-uniffi/Cargo.toml
version = "1.1.47"
version = "1.1.48"
edition = "2021"
build = "build.rs"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@
"message": "message",
"expiration": {
"discriminator": "expireAtTime",
"unixTimestampSeconds": "2023-09-11T16:05:56.000Z"
"unixTimestampSeconds": 1730999831257
}
}
},
Expand Down
11 changes: 6 additions & 5 deletions crates/sargon/src/core/types/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl HasSampleValues for Instant {
mod tests {
use super::*;
use crate::prelude::*;
use std::time::Duration;

#[test]
fn into_from_scrypto() {
Expand Down Expand Up @@ -111,12 +112,12 @@ mod tests {

#[test]
fn from_timestamp() {
let timestamp = Timestamp::now_utc();
let seconds_since_unix_epoch = timestamp
.duration_since(Timestamp::UNIX_EPOCH)
.as_seconds_f64() as i64;
let timestamp = Timestamp::UNIX_EPOCH;
let instant = Instant::from(timestamp);
assert_eq!(instant.seconds_since_unix_epoch, 0);

assert_eq!(instant.seconds_since_unix_epoch, seconds_since_unix_epoch);
let timestamp = Timestamp::UNIX_EPOCH.add(Duration::from_secs(300));
let instant = Instant::from(timestamp);
assert_eq!(instant.seconds_since_unix_epoch, 300);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::prelude::*;
#[serde(rename_all = "camelCase")]
pub struct DappToWalletInteractionSubintentExpireAtTime {
/// The unix timestamp in seconds when the subintent expires.
pub unix_timestamp_seconds: Timestamp,
pub unix_timestamp_seconds: u64,
}

impl From<Timestamp> for DappToWalletInteractionSubintentExpireAtTime {
fn from(unix_timestamp_seconds: Timestamp) -> Self {
impl From<u64> for DappToWalletInteractionSubintentExpireAtTime {
fn from(unix_timestamp_seconds: u64) -> Self {
Self {
unix_timestamp_seconds,
}
Expand All @@ -19,13 +19,13 @@ impl From<Timestamp> for DappToWalletInteractionSubintentExpireAtTime {
impl HasSampleValues for DappToWalletInteractionSubintentExpireAtTime {
fn sample() -> Self {
Self {
unix_timestamp_seconds: Timestamp::sample(),
unix_timestamp_seconds: 1730999831257,
}
}

fn sample_other() -> Self {
Self {
unix_timestamp_seconds: Timestamp::sample_other(),
unix_timestamp_seconds: 1730999850000,
}
}
}
Expand All @@ -50,6 +50,6 @@ mod tests {

#[test]
fn from() {
assert_eq!(SUT::from(Timestamp::sample()), SUT::sample());
assert_eq!(SUT::from(1730999831257), SUT::sample());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,37 @@ impl HasSampleValues for DappToWalletInteractionSubintentExpiration {
}
}

impl DappToWalletInteractionSubintentExpiration {
pub fn get_status(
&self,
) -> DappToWalletInteractionSubintentExpirationStatus {
match self {
Self::AtTime(expiration) => {
let now = seconds_since_unix_epoch();
let in_thirty_seconds = now + 30;
if expiration.unix_timestamp_seconds < now {
DappToWalletInteractionSubintentExpirationStatus::Expired
} else if expiration.unix_timestamp_seconds < in_thirty_seconds
{
DappToWalletInteractionSubintentExpirationStatus::ExpirationTooClose
} else {
DappToWalletInteractionSubintentExpirationStatus::Valid
}
}
Self::AfterDelay(_) => {
DappToWalletInteractionSubintentExpirationStatus::Valid
}
}
}
}

// Returns the amounts of seconds since the Unix epoch.
pub fn seconds_since_unix_epoch() -> u64 {
Timestamp::now_utc()
.duration_since(Timestamp::UNIX_EPOCH)
.as_seconds_f64() as u64
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -62,7 +93,7 @@ mod tests {
r#"
{
"discriminator": "expireAtTime",
"unixTimestampSeconds": "2023-09-11T16:05:56.000Z"
"unixTimestampSeconds": 1730999831257
}
"#,
);
Expand All @@ -77,4 +108,56 @@ mod tests {
"#,
);
}

#[test]
fn status() {
let now = Timestamp::now_utc();

// AtTime which has already expired
let now = seconds_since_unix_epoch();
let past = now - 30;
let expiration = SUT::AtTime(past.into());
assert_eq!(
expiration.get_status(),
DappToWalletInteractionSubintentExpirationStatus::Expired
);

// AtTime which is less than 30 seconds from expiration
let in_ten_seconds = now + 10;
let expiration = DappToWalletInteractionSubintentExpiration::AtTime(
DappToWalletInteractionSubintentExpireAtTime {
unix_timestamp_seconds: in_ten_seconds,
},
);
assert_eq!(
expiration.get_status(),
DappToWalletInteractionSubintentExpirationStatus::ExpirationTooClose
);

// AtTime which is more than 30 seconds from expiration
let in_forty_seconds = now + 40;
let expiration = DappToWalletInteractionSubintentExpiration::AtTime(
DappToWalletInteractionSubintentExpireAtTime {
unix_timestamp_seconds: in_forty_seconds,
},
);
assert_eq!(
expiration.get_status(),
DappToWalletInteractionSubintentExpirationStatus::Valid
);

// AfterDelay is always Valid, either in 10 minutes
let expiration = SUT::AfterDelay(600.into());
assert_eq!(
expiration.get_status(),
DappToWalletInteractionSubintentExpirationStatus::Valid
);

// .. or in 15 seconds
let expiration = SUT::AfterDelay(15.into());
assert_eq!(
expiration.get_status(),
DappToWalletInteractionSubintentExpirationStatus::Valid
);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod after_delay;
mod at_time;
mod expiration;
mod status;

pub use after_delay::*;
pub use at_time::*;
pub use expiration::*;
pub use status::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::prelude::*;

/// An enum that represents the expiration status of a subintent at a given time.
///
/// Useful for determining if a subintent is still valid at the moment the Host apps
/// receive the corresponding request.
#[derive(Debug, Clone, PartialEq)]
pub enum DappToWalletInteractionSubintentExpirationStatus {
/// The subintent hasn't expired yet
Valid,

/// The subintent is too close to its expiration. Although it hasn't expired yet, the Host apps
/// shouldn't allow the user dealing with it.
ExpirationTooClose,

/// The subintent has already expired.
Expired,
}

impl HasSampleValues for DappToWalletInteractionSubintentExpirationStatus {
fn sample() -> Self {
Self::Valid
}

fn sample_other() -> Self {
Self::ExpirationTooClose
}
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(clippy::upper_case_acronyms)]
type SUT = DappToWalletInteractionSubintentExpirationStatus;

#[test]
fn equality() {
assert_eq!(SUT::sample(), SUT::sample());
assert_eq!(SUT::sample_other(), SUT::sample_other());
}

#[test]
fn inequality() {
assert_ne!(SUT::sample(), SUT::sample_other());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ mod tests {
"message" : "message",
"expiration": {
"discriminator": "expireAtTime",
"unixTimestampSeconds": "2023-09-11T16:05:56.000Z"
"unixTimestampSeconds": 1730999831257
}
}
"#,
Expand Down
Loading

0 comments on commit 456272e

Please sign in to comment.