Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: xcm transfer rate limit pallet #561

Merged
merged 88 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from 84 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
a031bef
patch cumulus to xcmp queue version with defer filter
apopiak Apr 17, 2023
cb67a62
cargo lock changes
apopiak Apr 17, 2023
c5a52ca
add xcm-rate-limiter pallet base
dmoka Apr 17, 2023
9dd2264
add first failing test for tracking incoming asset liquidity
dmoka Apr 17, 2023
821a30f
add first implementation for tracking asset volume
dmoka Apr 18, 2023
45383d8
refactor rate limiter and test, add teleported asset case
apopiak Apr 18, 2023
29c5a1b
adjust runtime-benchmark feature in cargo toml
apopiak Apr 18, 2023
c5bc3f0
defer xcm with duration when rate limit exceeded
dmoka Apr 18, 2023
fa93624
add logic for setting rate limit per asset
dmoka Apr 18, 2023
b2fe0d0
add todos
dmoka Apr 18, 2023
bbda095
add root check for set limit
dmoka Apr 18, 2023
aba2538
started building a deffered message queue logic
jak-pan Apr 18, 2023
d52784d
WIP - add calculation for deferred by, based on block time difference
dmoka Apr 18, 2023
dbe3ad6
add first implementation for calculatin deferred duration
dmoka Apr 19, 2023
47db0b2
implement decay of accumulated amounts
apopiak Apr 19, 2023
87d9901
added deferring logic to XCMDeferFilter
jak-pan Apr 19, 2023
1919aa0
use associated types to get rate limit per asset
apopiak Apr 19, 2023
baa873d
add notes and todos + slight refactor
apopiak Apr 19, 2023
40339ef
add todo
dmoka Apr 20, 2023
0209bed
remove rate limits managing as the rate limits are stored in asset re…
dmoka Apr 20, 2023
7fdd188
remove unused events and error specs
dmoka Apr 20, 2023
12d0dd3
add doc for storage
dmoka Apr 20, 2023
fe0b7ad
add dedicated type for storing accumulated liquidity
dmoka Apr 20, 2023
56949fb
add missing doc
dmoka Apr 20, 2023
d0f86da
renaming
dmoka Apr 20, 2023
d60f624
renaming
dmoka Apr 20, 2023
783c545
add missing assertions
dmoka Apr 20, 2023
db2da9c
ignore not used params
dmoka Apr 20, 2023
d3ac7b1
avoid redoing computation when calculating deferred duration
dmoka Apr 20, 2023
0c1fb67
implement MaxDeferDuration
apopiak Apr 20, 2023
bc37735
use RelayChainBlockNumber for durations everywhere
apopiak Apr 20, 2023
7f89787
add missing doc comment
dmoka Apr 20, 2023
66d7e32
implement xcm rate limiter for runtime
dmoka Apr 20, 2023
aae8b32
write and fix xcm rate limiter integration test (was missing in runtime)
apopiak Apr 20, 2023
78e528d
formatting
apopiak Apr 20, 2023
de3a599
update teleport test for rate limiter
apopiak Apr 20, 2023
f84f41c
test for v2 conversion and defer for max when not able to convert
apopiak Apr 20, 2023
3a913a4
formatting
apopiak Apr 20, 2023
e26e4b3
check message hash
apopiak May 8, 2023
39a6608
Merge branch 'v0.9.38' into feat/xcm-defer
apopiak May 8, 2023
cf1e223
update cargo.lock
apopiak May 8, 2023
6cdabef
run and apply cargo fix
apopiak May 8, 2023
b8b0641
formatting
apopiak May 8, 2023
fb67a91
add missing parameter
apopiak May 8, 2023
9db7c29
more formatting and similar fixes
apopiak May 8, 2023
72eb591
make clippy even happier
apopiak May 8, 2023
5f735b6
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 1, 2023
e410fb4
formatting
apopiak Jun 1, 2023
c4985fd
bump crate versions
apopiak Jun 1, 2023
e4d3d3b
activate std for runtime deps in integration tests
apopiak Jun 5, 2023
90f8fc4
update cargo.lock
apopiak Jun 5, 2023
5edd867
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 5, 2023
45f35b6
adjust integation test to new relay block number source
apopiak Jun 5, 2023
41d6f3c
extract rate_limiter math to math package
apopiak Jun 5, 2023
8943213
Merge branch 'master' into feat/xcm-defer
apopiak Jun 5, 2023
7504d1a
move RelayChainBlockNumberProvider and HashProvider to adapters
apopiak Jun 5, 2023
0c9f202
add frame_system dep
apopiak Jun 5, 2023
a24fa51
use RelayChainBlockNumber type in adapter
apopiak Jun 5, 2023
8efe496
use default weight in mock
apopiak Jun 14, 2023
3790fe0
update cumulus to fixed benchmark version of xcmp queue
apopiak Jun 14, 2023
b421c53
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 14, 2023
a5d7811
bump crate versions
apopiak Jun 14, 2023
5dcf301
bump runtime version
apopiak Jun 14, 2023
06213a2
bump more crates
apopiak Jun 14, 2023
eb3db77
add doc comments and another prop test
apopiak Jun 14, 2023
9522d1c
update xcmp_queue to new version with weights and add benchmarked wei…
apopiak Jun 14, 2023
0b0cad9
update xcmp queue
apopiak Jun 14, 2023
f29655d
bump common runtime
apopiak Jun 14, 2023
e4be8cd
return approximate weight consumed by deferred_by
apopiak Jun 14, 2023
6697241
add readme
dmoka Jun 14, 2023
91e1ddc
remove unused WeightInfo
apopiak Jun 15, 2023
b4b86a9
remove unused dep
apopiak Jun 15, 2023
88ad136
track all assets in incoming instructions and defer based on max defe…
apopiak Jun 15, 2023
fe4f8a0
address review comments
apopiak Jun 15, 2023
e58df7a
add test for withdraw to make sure it's not tracked
apopiak Jun 15, 2023
ba7ffd4
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 19, 2023
a9fcbb8
bump crate versions
apopiak Jun 19, 2023
c68f143
reset otc version
apopiak Jun 26, 2023
c402513
update cargo.lock
apopiak Jun 26, 2023
30f171f
add test for executing deferred messages
apopiak Jun 26, 2023
421a305
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 26, 2023
6efd823
query fee from treasurey account instead of hard-coding
apopiak Jun 26, 2023
80614f5
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jun 28, 2023
4d2f1ab
bump crate versions
apopiak Jun 28, 2023
1119b13
remove xcm rate limiter from runtime
apopiak Jul 12, 2023
7eb04cd
correct rate limiter Cargo.toml
apopiak Jul 12, 2023
3a1534d
Merge branch 'master' of github.com:galacticcouncil/HydraDX-node into…
apopiak Jul 12, 2023
3895821
bump crate versions
apopiak Jul 12, 2023
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
667 changes: 405 additions & 262 deletions Cargo.lock

Large diffs are not rendered by default.

196 changes: 127 additions & 69 deletions Cargo.toml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "runtime-integration-tests"
version = "1.7.16"
version = "1.8.0"
description = "Integration tests"
authors = ["GalacticCouncil"]
edition = "2021"
Expand Down
8 changes: 4 additions & 4 deletions integration-tests/src/cross_chain_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn hydra_should_receive_asset_when_transferred_from_acala() {
Hydra::execute_with(|| {
assert_ok!(hydradx_runtime::AssetRegistry::set_location(
hydradx_runtime::RuntimeOrigin::root(),
1,
ACA,
hydradx_runtime::AssetLocation(MultiLocation::new(1, X2(Parachain(ACALA_PARA_ID), GeneralIndex(0))))
));
});
Expand Down Expand Up @@ -144,11 +144,11 @@ fn hydra_should_receive_asset_when_transferred_from_acala() {
let fee = 400641025641;
Hydra::execute_with(|| {
assert_eq!(
hydradx_runtime::Tokens::free_balance(1, &AccountId::from(BOB)),
1_030 * UNITS - fee
hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)),
30 * UNITS - fee
);
assert_eq!(
hydradx_runtime::Tokens::free_balance(1, &hydradx_runtime::Treasury::account_id()),
hydradx_runtime::Tokens::free_balance(ACA, &hydradx_runtime::Treasury::account_id()),
fee // fees should go to treasury
);
});
Expand Down
1 change: 1 addition & 0 deletions integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod otc;
mod polkadot_test_net;
mod transact_call_filter;
mod vesting;
mod xcm_rate_limiter;

#[macro_export]
macro_rules! assert_balance {
Expand Down
5 changes: 4 additions & 1 deletion integration-tests/src/polkadot_test_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub const DAI: AssetId = 2;
pub const DOT: AssetId = 3;
pub const ETH: AssetId = 4;
pub const BTC: AssetId = 5;
pub const ACA: AssetId = 6;

decl_test_relay_chain! {
pub struct PolkadotRelay {
Expand Down Expand Up @@ -196,6 +197,7 @@ pub fn hydra_ext() -> sp_io::TestExternalities {
(b"DOT".to_vec(), 1_000u128, Some(DOT)),
(b"ETH".to_vec(), 1_000u128, Some(ETH)),
(b"BTC".to_vec(), 1_000u128, Some(BTC)),
(b"ACA".to_vec(), 1_000u128, Some(ACA)),
],
native_asset_name: b"HDX".to_vec(),
native_existential_deposit: existential_deposit,
Expand Down Expand Up @@ -242,6 +244,7 @@ pub fn hydra_ext() -> sp_io::TestExternalities {
currencies: vec![
(LRNA, Price::from(1)),
(DAI, Price::from(1)),
(ACA, Price::from(1)),
(BTC, Price::from_inner(134_000_000)),
],
account_currencies: vec![],
Expand Down Expand Up @@ -322,7 +325,7 @@ pub fn last_hydra_events(n: usize) -> Vec<hydradx_runtime::RuntimeEvent> {
}

pub fn expect_hydra_events(e: Vec<hydradx_runtime::RuntimeEvent>) {
assert_eq!(last_hydra_events(e.len()), e);
pretty_assertions::assert_eq!(last_hydra_events(e.len()), e);
}

pub fn set_relaychain_block_number(number: BlockNumber) {
Expand Down
244 changes: 244 additions & 0 deletions integration-tests/src/xcm_rate_limiter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#![cfg(test)]

use crate::polkadot_test_net::*;

use common_runtime::Weight;
use frame_support::assert_ok;
use orml_traits::currency::MultiCurrency;
use pallet_asset_registry::AssetType;
use polkadot_xcm::prelude::*;
use xcm_emulator::TestExt;

/// Returns the message hash in the `XcmpMessageSent` event at the `n`th last event (1-indexed, so if the second to last
/// event has the hash, pass `2`);
fn get_message_hash_from_event(n: usize) -> Option<[u8; 32]> {
use cumulus_pallet_xcmp_queue::Event;
use hydradx_runtime::RuntimeEvent;
let RuntimeEvent::XcmpQueue(Event::XcmpMessageSent { message_hash }) = &last_hydra_events(n)[0] else {
panic!("expecting to find message sent event");
};
*message_hash
}

#[test]
fn xcm_rate_limiter_should_limit_aca_when_limit_is_exceeded() {
// Arrange
TestNet::reset();

Hydra::execute_with(|| {
assert_ok!(hydradx_runtime::AssetRegistry::set_location(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
hydradx_runtime::AssetLocation(MultiLocation::new(1, X2(Parachain(ACALA_PARA_ID), GeneralIndex(0))))
));

// set an xcm rate limit
assert_ok!(hydradx_runtime::AssetRegistry::update(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
b"ACA".to_vec(),
AssetType::Token,
None,
Some(50 * UNITS),
));

assert_eq!(hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)), 0);
});

let amount = 100 * UNITS;
let mut message_hash = None;
Acala::execute_with(|| {
// Act
assert_ok!(hydradx_runtime::XTokens::transfer(
hydradx_runtime::RuntimeOrigin::signed(ALICE.into()),
0,
amount,
Box::new(
MultiLocation::new(
1,
X2(
Junction::Parachain(HYDRA_PARA_ID),
Junction::AccountId32 { id: BOB, network: None }
)
)
.into()
),
WeightLimit::Limited(Weight::from_ref_time(399_600_000_000))
));

message_hash = get_message_hash_from_event(2);

// Assert
assert_eq!(
hydradx_runtime::Balances::free_balance(&AccountId::from(ALICE)),
ALICE_INITIAL_NATIVE_BALANCE - amount
);
});

Hydra::execute_with(|| {
expect_hydra_events(vec![
cumulus_pallet_xcmp_queue::Event::XcmDeferred {
sender: ACALA_PARA_ID.into(),
sent_at: 3,
deferred_to: 604, // received at 4 plus 600 blocks of deferral
message_hash,
}
.into(),
pallet_relaychain_info::Event::CurrentBlockNumbers {
parachain_block_number: 1,
relaychain_block_number: 5,
}
.into(),
]);
assert_eq!(hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)), 0);
});
}

#[test]
fn xcm_rate_limiter_should_not_limit_aca_when_limit_is_not_exceeded() {
// Arrange
TestNet::reset();

Hydra::execute_with(|| {
assert_ok!(hydradx_runtime::AssetRegistry::set_location(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
hydradx_runtime::AssetLocation(MultiLocation::new(1, X2(Parachain(ACALA_PARA_ID), GeneralIndex(0))))
));

// set an xcm rate limit
assert_ok!(hydradx_runtime::AssetRegistry::update(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
b"ACA".to_vec(),
AssetType::Token,
None,
Some(101 * UNITS),
));
});

let amount = 100 * UNITS;
Acala::execute_with(|| {
// Act
assert_ok!(hydradx_runtime::XTokens::transfer(
hydradx_runtime::RuntimeOrigin::signed(ALICE.into()),
0,
amount,
Box::new(
MultiLocation::new(
1,
X2(
Junction::Parachain(HYDRA_PARA_ID),
Junction::AccountId32 { id: BOB, network: None }
)
)
.into()
),
WeightLimit::Limited(Weight::from_ref_time(399_600_000_000))
));

// Assert
assert_eq!(
hydradx_runtime::Balances::free_balance(&AccountId::from(ALICE)),
ALICE_INITIAL_NATIVE_BALANCE - amount
);
});

Hydra::execute_with(|| {
let fee = hydradx_runtime::Tokens::free_balance(ACA, &hydradx_runtime::Treasury::account_id());
assert_eq!(
hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)),
amount - fee
);
});
}

#[test]
fn deferred_messages_should_be_executable_by_root() {
// Arrange
TestNet::reset();

Hydra::execute_with(|| {
assert_ok!(hydradx_runtime::AssetRegistry::set_location(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
hydradx_runtime::AssetLocation(MultiLocation::new(1, X2(Parachain(ACALA_PARA_ID), GeneralIndex(0))))
));

// set an xcm rate limit
assert_ok!(hydradx_runtime::AssetRegistry::update(
hydradx_runtime::RuntimeOrigin::root(),
ACA,
b"ACA".to_vec(),
AssetType::Token,
None,
Some(50 * UNITS),
));

assert_eq!(hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)), 0);
});

let amount = 100 * UNITS;
let mut message_hash = None;
let max_weight = Weight::from_ref_time(399_600_000_000);
Acala::execute_with(|| {
// Act
assert_ok!(hydradx_runtime::XTokens::transfer(
hydradx_runtime::RuntimeOrigin::signed(ALICE.into()),
0,
amount,
Box::new(
MultiLocation::new(
1,
X2(
Junction::Parachain(HYDRA_PARA_ID),
Junction::AccountId32 { id: BOB, network: None }
)
)
.into()
),
WeightLimit::Limited(max_weight),
));

message_hash = get_message_hash_from_event(2);

// Assert
assert_eq!(
hydradx_runtime::Balances::free_balance(&AccountId::from(ALICE)),
ALICE_INITIAL_NATIVE_BALANCE - amount
);
});

Hydra::execute_with(|| {
expect_hydra_events(vec![
cumulus_pallet_xcmp_queue::Event::XcmDeferred {
sender: ACALA_PARA_ID.into(),
sent_at: 3,
deferred_to: 604, // received at 4 plus 600 blocks of deferral
message_hash,
}
.into(),
pallet_relaychain_info::Event::CurrentBlockNumbers {
parachain_block_number: 1,
relaychain_block_number: 5,
}
.into(),
]);
assert_eq!(hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)), 0);

set_relaychain_block_number(604);

assert_eq!(hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)), 0);
assert_ok!(hydradx_runtime::XcmpQueue::service_deferred(
hydradx_runtime::RuntimeOrigin::root(),
max_weight,
ACALA_PARA_ID.into(),
));

let fee = hydradx_runtime::Tokens::free_balance(ACA, &hydradx_runtime::Treasury::account_id());
assert_eq!(
hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)),
amount - fee
);
});
}
2 changes: 1 addition & 1 deletion math/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = 'Apache-2.0'
name = "hydra-dx-math"
description = "A collection of utilities to make performing liquidity pool calculations more convenient."
repository = '/~https://github.com/galacticcouncil/hydradx-math'
version = "7.2.0"
version = "7.2.1"

[dependencies]
primitive-types = {default-features = false, version = '0.12.0'}
Expand Down
1 change: 1 addition & 0 deletions math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod lbp;
pub mod liquidity_mining;
pub mod omnipool;
pub mod omnipool_subpools;
pub mod rate_limiter;
pub mod ratio;
pub mod stableswap;
pub mod support;
Expand Down
45 changes: 45 additions & 0 deletions math/src/rate_limiter/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use sp_arithmetic::traits::SaturatedConversion;

use crate::types::Balance;

/// Calculate how long to defer something based on ratio between `rate_limit` and `total_accumulated`.
/// Will return 0 if `total_accumulated` is less than `rate_limit`.
/// 2x `rate_limit` accumulated tokens will be deferred by `defer_duration`.
pub fn calculate_deferred_duration(defer_duration: u32, rate_limit: Balance, total_accumulated: Balance) -> u32 {
let defer_duration: u128 = defer_duration.max(1).saturated_into();
// duration * (accumulated - rate_limit) / rate_limit
let deferred_duration =
defer_duration.saturating_mul(total_accumulated.saturating_sub(rate_limit)) / rate_limit.max(1);

deferred_duration.saturated_into()
}

/// Calculate how much balance has accumulated by decaying the previous `accumulated_amount` based on
/// `blocks_since_last_update` and adding `incoming_amount`.
pub fn calculate_new_accumulated_amount(
defer_duration: u32,
rate_limit: Balance,
incoming_amount: Balance,
accumulated_amount: Balance,
blocks_since_last_update: u32,
) -> Balance {
incoming_amount.saturating_add(decay_accumulated_amount(
defer_duration,
rate_limit,
accumulated_amount,
blocks_since_last_update,
))
}

/// Calculate how much the `accumulated_amount` has decayed based on `blocks_since_last_update` and `rate_limit`.
pub fn decay_accumulated_amount(
defer_duration: u32,
rate_limit: Balance,
accumulated_amount: Balance,
blocks_since_last_update: u32,
) -> Balance {
let defer_duration: u128 = defer_duration.max(1).saturated_into();
// acc - rate_limit * blocks / duration
accumulated_amount
.saturating_sub(rate_limit.saturating_mul(blocks_since_last_update.saturated_into()) / defer_duration)
}
6 changes: 6 additions & 0 deletions math/src/rate_limiter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod math;

#[cfg(test)]
mod tests;

pub use math::*;
Loading