Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
498d999
versioned + salted nonces added
hlgltvnnk Sep 25, 2025
080a077
nonce verifier added
hlgltvnnk Sep 26, 2025
04567c2
salt management methods added
hlgltvnnk Sep 26, 2025
f29f537
tests updated
hlgltvnnk Sep 26, 2025
e9ce10e
valid salts format upd: all previous salts stored in map
hlgltvnnk Sep 26, 2025
28a4618
nonce verifier removed, logic moved to state
hlgltvnnk Sep 29, 2025
9a34e35
nonce tests updated + invalidate salt method added
hlgltvnnk Sep 29, 2025
792717d
salt manager tests added
hlgltvnnk Sep 30, 2025
c5d43f1
fee manager tests added
hlgltvnnk Sep 30, 2025
06d2459
state versioning added + clippy lints fixed
hlgltvnnk Sep 30, 2025
efae3c5
refactoring
hlgltvnnk Oct 1, 2025
ecac381
changelog added + minor fixes
hlgltvnnk Oct 1, 2025
a5c2d45
minor edit
hlgltvnnk Oct 1, 2025
40a10ad
refactoring
hlgltvnnk Oct 1, 2025
0728277
minor edits
hlgltvnnk Oct 1, 2025
b294c34
changes according to review
hlgltvnnk Oct 6, 2025
cf75a19
minor edit
hlgltvnnk Oct 6, 2025
dde795b
changes according to review #2
hlgltvnnk Oct 6, 2025
8060aba
contract versioning addded
hlgltvnnk Oct 7, 2025
b7cee39
minor fixs + missing files
hlgltvnnk Oct 7, 2025
7493e1e
naming upd + minor fixes
hlgltvnnk Oct 7, 2025
3d200e5
state migration updated + refactored
hlgltvnnk Oct 7, 2025
56da699
refactoring
hlgltvnnk Oct 7, 2025
28d9174
changes according to review #3
hlgltvnnk Oct 7, 2025
1ae5bcb
refactoring
hlgltvnnk Oct 8, 2025
494abdf
salt derivation updated + naming upd
hlgltvnnk Oct 8, 2025
2961a7e
minor updates
hlgltvnnk Oct 8, 2025
bb28ed3
MaybeVersionedNonce removed
hlgltvnnk Oct 8, 2025
731224d
refactoring
hlgltvnnk Oct 8, 2025
e550ff1
missing file from prev commit
hlgltvnnk Oct 8, 2025
3acfda8
minor edits
hlgltvnnk Oct 8, 2025
5385e75
refactoring + garbage collector role added
hlgltvnnk Oct 9, 2025
6087055
clean nonces made payable
hlgltvnnk Oct 9, 2025
010f6ae
fix
hlgltvnnk Oct 9, 2025
f30b752
test fix
hlgltvnnk Oct 9, 2025
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
10 changes: 9 additions & 1 deletion core/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use near_sdk::{AccountIdRef, near};
use serde_with::serde_as;
use std::borrow::Cow;

use crate::Nonce;
use crate::{Nonce, Salt};

#[must_use = "make sure to `.emit()` this event"]
#[near(serializers = [json])]
Expand Down Expand Up @@ -63,3 +63,11 @@ impl NonceEvent {
Self { nonce }
}
}

#[must_use = "make sure to `.emit()` this event"]
#[near(serializers = [json])]
#[derive(Debug, Clone)]
pub struct SaltRotationEvent {
pub old_salt: Salt,
pub new_salt: Salt,
}
7 changes: 2 additions & 5 deletions core/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use self::{inspector::*, state::*};
use defuse_crypto::{Payload, SignedPayload};

use crate::{
DefuseError, ExpirableNonce, Result,
DefuseError, Result,
intents::{DefuseIntents, ExecutableIntent},
payload::{DefusePayload, ExtractDefusePayload, multi::MultiPayload},
};
Expand Down Expand Up @@ -64,10 +64,6 @@ where

self.inspector.on_deadline(deadline);

if ExpirableNonce::maybe_from(nonce).is_some_and(|n| deadline > n.deadline) {
return Err(DefuseError::DeadlineGreaterThanNonce);
}

// make sure message is still valid
if deadline.has_expired() {
return Err(DefuseError::DeadlineExpired);
Expand All @@ -79,6 +75,7 @@ where
}

// commit nonce
self.state.verify_intent_nonce(nonce, deadline)?;
self.state.commit_nonce(signer_id.clone(), nonce)?;

intents.execute_intent(&signer_id, self, hash)?;
Expand Down
10 changes: 9 additions & 1 deletion core/src/engine/state/cached.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
DefuseError, Nonce, Nonces, Result,
Deadline, DefuseError, Nonce, Nonces, Result,
amounts::Amounts,
fees::Pips,
intents::{
Expand Down Expand Up @@ -119,6 +119,14 @@ where
.is_some_and(|a| a.auth_by_predecessor_id_toggled);
was_enabled ^ toggled
}

fn verify_intent_nonce(&self, nonce: Nonce, intent_deadline: Deadline) -> Result<()> {
self.view.verify_intent_nonce(nonce, intent_deadline)
}

fn is_nonce_cleanable(&self, nonce: Nonce) -> Result<bool> {
self.view.is_nonce_cleanable(nonce)
}
}

impl<W> State for CachedState<W>
Expand Down
12 changes: 11 additions & 1 deletion core/src/engine/state/deltas.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
DefuseError, Nonce, Result,
Deadline, DefuseError, Nonce, Result,
amounts::Amounts,
fees::Pips,
intents::{
Expand Down Expand Up @@ -96,6 +96,16 @@ where
fn is_auth_by_predecessor_id_enabled(&self, account_id: &AccountIdRef) -> bool {
self.state.is_auth_by_predecessor_id_enabled(account_id)
}

#[inline]
fn verify_intent_nonce(&self, nonce: Nonce, intent_deadline: Deadline) -> Result<()> {
self.state.verify_intent_nonce(nonce, intent_deadline)
}

#[inline]
fn is_nonce_cleanable(&self, nonce: Nonce) -> Result<bool> {
self.state.is_nonce_cleanable(nonce)
}
}

impl<S> State for Deltas<S>
Expand Down
8 changes: 7 additions & 1 deletion core/src/engine/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub mod cached;
pub mod deltas;

use crate::{
Nonce, Result,
Deadline, Nonce, Result,
fees::Pips,
intents::{
auth::AuthCall,
Expand Down Expand Up @@ -42,6 +42,12 @@ pub trait StateView {
/// Returns whether authentication by `PREDECESSOR_ID` is enabled.
fn is_auth_by_predecessor_id_enabled(&self, account_id: &AccountIdRef) -> bool;

/// Verifies whether the nonce can be committed
fn verify_intent_nonce(&self, nonce: Nonce, intent_deadline: Deadline) -> Result<()>;

/// Returns whether the nonce can be cleaned up
fn is_nonce_cleanable(&self, nonce: Nonce) -> Result<bool>;

#[inline]
fn cached(self) -> CachedState<Self>
where
Expand Down
6 changes: 6 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub enum DefuseError {
#[error("nonce was already expired")]
NonceExpired,

#[error("invalid nonce")]
InvalidNonce,

#[error("public key '{1}' already exists for account '{0}'")]
PublicKeyExists(AccountId, PublicKey),

Expand All @@ -66,4 +69,7 @@ pub enum DefuseError {

#[error("wrong verifying_contract")]
WrongVerifyingContract,

#[error("invalid salt")]
InvalidSalt,
}
6 changes: 5 additions & 1 deletion core/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use derive_more::derive::From;
use near_sdk::{near, serde::Deserialize};

use crate::{
accounts::{AccountEvent, NonceEvent, PublicKeyEvent},
accounts::{AccountEvent, NonceEvent, PublicKeyEvent, SaltRotationEvent},
fees::{FeeChangedEvent, FeeCollectorChangedEvent},
intents::{
IntentEvent,
Expand All @@ -30,6 +30,10 @@ pub enum DefuseEvent<'a> {
#[event_version("0.3.0")]
FeeCollectorChanged(FeeCollectorChangedEvent<'a>),

// TODO: which version should this be?
#[event_version("0.1.0")]
SaltRotationEvent(SaltRotationEvent),

#[event_version("0.3.0")]
Transfer(Cow<'a, [IntentEvent<AccountEvent<'a, Cow<'a, Transfer>>>]>),

Expand Down
166 changes: 0 additions & 166 deletions core/src/nonce.rs

This file was deleted.

61 changes: 61 additions & 0 deletions core/src/nonce/expirable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use defuse_borsh_utils::adapters::{As, TimestampNanoSeconds};
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};

use crate::Deadline;

/// Expirable nonces contain deadline which is 8 bytes of timestamp in nanoseconds
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "::near_sdk::borsh")]
pub struct ExpirableNonce<T>
where
T: BorshSerialize + BorshDeserialize,
{
#[borsh(
serialize_with = "As::<TimestampNanoSeconds>::serialize",
deserialize_with = "As::<TimestampNanoSeconds>::deserialize"
)]
pub deadline: Deadline,
pub nonce: T,
}

impl<T> ExpirableNonce<T>
where
T: BorshSerialize + BorshDeserialize,
{
pub const fn new(deadline: Deadline, nonce: T) -> Self {
Self { deadline, nonce }
}

#[inline]
pub fn has_expired(&self) -> bool {
self.deadline.has_expired()
}
}

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

use chrono::{Days, Utc};
use defuse_test_utils::random::random_bytes;
use rstest::rstest;

#[rstest]
fn expirable_test(random_bytes: Vec<u8>) {
let current_timestamp = Utc::now();
let mut u = arbitrary::Unstructured::new(&random_bytes);
let nonce: [u8; 24] = u.arbitrary().unwrap();

let expired = ExpirableNonce::new(
Deadline::new(current_timestamp.checked_sub_days(Days::new(1)).unwrap()),
nonce,
);
assert!(expired.has_expired());

let not_expired = ExpirableNonce::new(
Deadline::new(current_timestamp.checked_add_days(Days::new(1)).unwrap()),
nonce,
);
assert!(!not_expired.has_expired());
}
}
Loading
Loading