From c7a6a47496b84fb6aa422978be5e9b2fc716703c Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 26 Aug 2025 14:27:16 +0200 Subject: [PATCH 1/4] introduce porteer nonce --- porteer/src/lib.rs | 38 +++++++++++++++++++++++++++++++++----- porteer/src/mock.rs | 2 ++ porteer/src/tests.rs | 22 ++++++++++++++++------ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/porteer/src/lib.rs b/porteer/src/lib.rs index a4396cc9..2807c91b 100644 --- a/porteer/src/lib.rs +++ b/porteer/src/lib.rs @@ -36,11 +36,14 @@ mod tests; pub mod weights; -use frame_support::transactional; -use sp_runtime::DispatchError; - pub use crate::weights::WeightInfo; +use frame_support::{transactional, Parameter}; pub use pallet::*; +use parity_scale_codec::MaxEncodedLen; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Member}, + DispatchError, +}; pub const LOG_TARGET: &str = "integritee::porteer"; @@ -183,7 +186,11 @@ pub mod pallet { /// Ported some tokens to the destination chain. PortedTokens { who: AccountIdOf, amount: BalanceOf }, /// Minted some tokens ported from another chain! - MintedPortedTokens { who: AccountIdOf, amount: BalanceOf }, + MintedPortedTokens { + who: AccountIdOf, + amount: BalanceOf, + sender_nonce: <::PortTokensToDestination as PortTokens>::Nonce, + }, /// Forwarded some minted tokens to another location. ForwardedPortedTokens { who: AccountIdOf, amount: BalanceOf, location: T::Location }, /// Failed to forward the tokens to the final destination. @@ -221,10 +228,15 @@ pub mod pallet { #[pallet::storage] pub(super) type WatchdogAccount = StorageValue<_, AccountIdOf, OptionQuery>; - /// The block number at which the last heartbeat was received. + /// The timestamp at which the last heartbeat was received. #[pallet::storage] pub(super) type LastHeartBeat = StorageValue<_, T::Moment, ValueQuery>; + /// The timestamp at which the last heartbeat was received. + #[pallet::storage] + pub(super) type PortTokensNonce = + StorageValue<_, <::PortTokensToDestination as PortTokens>::Nonce, ValueQuery>; + /// Entails the amount of fees needed at the respective hops. #[pallet::storage] pub(super) type XcmFeeConfig = @@ -389,10 +401,16 @@ pub mod pallet { Fortitude::Polite, )?; + let nonce = PortTokensNonce::::mutate(|n| { + *n = n.saturating_add(1u32.into()); + *n + }); + T::PortTokensToDestination::port_tokens( signer.clone(), amount, forward_tokens_to_location, + nonce, ) .map_err(|e| { log::error!(target: LOG_TARGET, "Port tokens error: {:?}", e); @@ -414,6 +432,7 @@ pub mod pallet { beneficiary: AccountIdOf, amount: BalanceOf, forward_tokens_to_location: Option, + sender_nonce: <::PortTokensToDestination as PortTokens>::Nonce, ) -> DispatchResult { let _signer = T::TokenSenderLocationOrigin::ensure_origin(origin)?; @@ -424,6 +443,7 @@ pub mod pallet { Self::deposit_event(Event::::MintedPortedTokens { who: beneficiary.clone(), amount, + sender_nonce, }); // Forward the tokens if desired @@ -455,6 +475,13 @@ pub trait PortTokens { type AccountId; type Balance; + type Nonce: Parameter + + Member + + AtLeast32BitUnsigned + + Default + + Copy + + MaybeSerializeDeserialize + + MaxEncodedLen; type Location; @@ -464,6 +491,7 @@ pub trait PortTokens { who: Self::AccountId, amount: Self::Balance, forward_tokens_to: Option, + nonce: Self::Nonce, ) -> Result<(), Self::Error>; } diff --git a/porteer/src/mock.rs b/porteer/src/mock.rs index 067ba909..ee799b83 100644 --- a/porteer/src/mock.rs +++ b/porteer/src/mock.rs @@ -117,6 +117,7 @@ pub struct MockPortTokens; impl PortTokens for MockPortTokens { type AccountId = AccountId; type Balance = Balance; + type Nonce = u32; type Location = TestLocation; type Error = DispatchError; @@ -124,6 +125,7 @@ impl PortTokens for MockPortTokens { _who: Self::AccountId, _amount: Self::Balance, _forward_tokens_to: Option, + _nonce: Self::Nonce, ) -> Result<(), Self::Error> { Ok(()) } diff --git a/porteer/src/tests.rs b/porteer/src/tests.rs index 38ce88b5..0759e8f5 100644 --- a/porteer/src/tests.rs +++ b/porteer/src/tests.rs @@ -271,6 +271,7 @@ fn port_tokens_system_test_works() { assert_ok!(Porteer::set_watchdog(RuntimeOrigin::signed(alice.clone()), bob.clone())); + assert_eq!(PortTokensNonce::::get(), 0); assert_eq!(LastHeartBeat::::get(), 0); let now = Timestamp::get(); @@ -292,6 +293,7 @@ fn port_tokens_system_test_works() { porteering_amount, None )); + assert_eq!(PortTokensNonce::::get(), 1); // Test that bridge stays enabled until the HeartbeatTimout Timestamp::set_timestamp(now + HeartBeatTimeout::get()); @@ -300,6 +302,7 @@ fn port_tokens_system_test_works() { porteering_amount, None )); + assert_eq!(PortTokensNonce::::get(), 2); // Bridge Send is disabled after HeartbeatTimeout has passed Timestamp::set_timestamp(now + HeartBeatTimeout::get() + 1); @@ -307,6 +310,8 @@ fn port_tokens_system_test_works() { Porteer::port_tokens(RuntimeOrigin::signed(alice.clone()), porteering_amount, None), Error::::WatchdogHeartbeatIsTooOld ); + // Nonce has not increased + assert_eq!(PortTokensNonce::::get(), 2); }) } @@ -366,12 +371,14 @@ fn minting_ported_tokens_works() { RuntimeOrigin::signed(alice.clone()), bob.clone(), mint_amount, - None + None, + 42 )); let expected_event = RuntimeEvent::Porteer(PorteerEvent::MintedPortedTokens { who: bob.clone(), amount: mint_amount, + sender_nonce: 42, }); assert!(System::events().iter().any(|a| a.event == expected_event)); @@ -385,7 +392,7 @@ fn minting_ported_tokens_errs_with_wrong_origin() { let bob = Keyring::Bob.to_account_id(); assert_noop!( - Porteer::mint_ported_tokens(RuntimeOrigin::signed(bob.clone()), bob, 1, None), + Porteer::mint_ported_tokens(RuntimeOrigin::signed(bob.clone()), bob, 1, None, 0), BadOrigin ); }) @@ -400,7 +407,7 @@ fn minting_ported_tokens_errs_when_receiving_disabled() { assert_ok!(Porteer::set_porteer_config(RuntimeOrigin::signed(alice.clone()), config)); assert_noop!( - Porteer::mint_ported_tokens(RuntimeOrigin::signed(alice.clone()), alice, 1, None), + Porteer::mint_ported_tokens(RuntimeOrigin::signed(alice.clone()), alice, 1, None, 0), Error::::PorteerOperationDisabled ); }) @@ -424,7 +431,8 @@ fn minting_ported_tokens_with_forwarding_works() { RuntimeOrigin::signed(alice.clone()), bob.clone(), mint_amount, - Some(WHITELISTED_LOCATION) + Some(WHITELISTED_LOCATION), + 0 )); // We keep the ED during forwarding @@ -448,7 +456,8 @@ fn minting_ported_tokens_with_forwarding_non_whitelisted_location_preserves_bala RuntimeOrigin::signed(alice.clone()), bob.clone(), mint_amount, - Some(WHITELISTED_LOCATION) + Some(WHITELISTED_LOCATION), + 0 )); let expected_event = RuntimeEvent::Porteer(PorteerEvent::IllegalForwardingLocation { @@ -480,7 +489,8 @@ fn minting_ported_tokens_with_forwarding_to_unsupported_location_preserves_balan RuntimeOrigin::signed(alice.clone()), bob.clone(), mint_amount, - Some(WHITELISTED_BUT_UNSUPPORTED_LOCATION) + Some(WHITELISTED_BUT_UNSUPPORTED_LOCATION), + 0 )); let expected_event = RuntimeEvent::Porteer(PorteerEvent::FailedToForwardTokens { From 15ba3c4c7eefd49f56a3d05c0958f66139f22043 Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 26 Aug 2025 14:33:15 +0200 Subject: [PATCH 2/4] rename sender_nonce to source_nonce to avoid ambiguity with the caller --- porteer/src/lib.rs | 6 +++--- porteer/src/tests.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/porteer/src/lib.rs b/porteer/src/lib.rs index 2807c91b..295552b1 100644 --- a/porteer/src/lib.rs +++ b/porteer/src/lib.rs @@ -189,7 +189,7 @@ pub mod pallet { MintedPortedTokens { who: AccountIdOf, amount: BalanceOf, - sender_nonce: <::PortTokensToDestination as PortTokens>::Nonce, + source_nonce: <::PortTokensToDestination as PortTokens>::Nonce, }, /// Forwarded some minted tokens to another location. ForwardedPortedTokens { who: AccountIdOf, amount: BalanceOf, location: T::Location }, @@ -432,7 +432,7 @@ pub mod pallet { beneficiary: AccountIdOf, amount: BalanceOf, forward_tokens_to_location: Option, - sender_nonce: <::PortTokensToDestination as PortTokens>::Nonce, + source_nonce: <::PortTokensToDestination as PortTokens>::Nonce, ) -> DispatchResult { let _signer = T::TokenSenderLocationOrigin::ensure_origin(origin)?; @@ -443,7 +443,7 @@ pub mod pallet { Self::deposit_event(Event::::MintedPortedTokens { who: beneficiary.clone(), amount, - sender_nonce, + source_nonce, }); // Forward the tokens if desired diff --git a/porteer/src/tests.rs b/porteer/src/tests.rs index 0759e8f5..f6594e6f 100644 --- a/porteer/src/tests.rs +++ b/porteer/src/tests.rs @@ -378,7 +378,7 @@ fn minting_ported_tokens_works() { let expected_event = RuntimeEvent::Porteer(PorteerEvent::MintedPortedTokens { who: bob.clone(), amount: mint_amount, - sender_nonce: 42, + source_nonce: 42, }); assert!(System::events().iter().any(|a| a.event == expected_event)); From 0dc02720596ba161a43d9b9551f11cf90909f91d Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 26 Aug 2025 15:19:43 +0200 Subject: [PATCH 3/4] simplify with type alias --- porteer/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/porteer/src/lib.rs b/porteer/src/lib.rs index 295552b1..484e50c8 100644 --- a/porteer/src/lib.rs +++ b/porteer/src/lib.rs @@ -66,6 +66,7 @@ pub mod pallet { pub type AccountIdOf = ::AccountId; pub type BalanceOf = <::Fungible as fungible::Inspect>>::Balance; + pub type PortTokensNonceOf = <::PortTokensToDestination as PortTokens>::Nonce; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] @@ -189,7 +190,7 @@ pub mod pallet { MintedPortedTokens { who: AccountIdOf, amount: BalanceOf, - source_nonce: <::PortTokensToDestination as PortTokens>::Nonce, + source_nonce: PortTokensNonceOf, }, /// Forwarded some minted tokens to another location. ForwardedPortedTokens { who: AccountIdOf, amount: BalanceOf, location: T::Location }, @@ -234,8 +235,7 @@ pub mod pallet { /// The timestamp at which the last heartbeat was received. #[pallet::storage] - pub(super) type PortTokensNonce = - StorageValue<_, <::PortTokensToDestination as PortTokens>::Nonce, ValueQuery>; + pub(super) type PortTokensNonce = StorageValue<_, PortTokensNonceOf, ValueQuery>; /// Entails the amount of fees needed at the respective hops. #[pallet::storage] @@ -432,7 +432,7 @@ pub mod pallet { beneficiary: AccountIdOf, amount: BalanceOf, forward_tokens_to_location: Option, - source_nonce: <::PortTokensToDestination as PortTokens>::Nonce, + source_nonce: PortTokensNonceOf, ) -> DispatchResult { let _signer = T::TokenSenderLocationOrigin::ensure_origin(origin)?; From 82438ef30a35366d52de242e801116aaabd54fda Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 26 Aug 2025 15:23:23 +0200 Subject: [PATCH 4/4] fix benchmark --- porteer/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porteer/src/benchmarking.rs b/porteer/src/benchmarking.rs index e44dadae..8728c777 100644 --- a/porteer/src/benchmarking.rs +++ b/porteer/src/benchmarking.rs @@ -85,7 +85,7 @@ benchmarks! { >::set_balance(&bob, 0u32.into()); let location = ::BenchmarkHelper::get_whitelisted_location(); - }: _(RawOrigin::Root, bob.clone(), mint_amount, Some(location)) + }: _(RawOrigin::Root, bob.clone(), mint_amount, Some(location), 0u32.into()) verify { assert_eq!(>::balance(&bob), mint_amount); }