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: Connectors v2 message PoC #1292

Merged
merged 35 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
65a3d17
feat: update Message enum
wischli Mar 28, 2023
690430e
feat: add (dummy) Into<u128> for CurrencyId
wischli Mar 28, 2023
b5eb58c
tests: apply changes to existing
wischli Mar 29, 2023
4588b91
refactor: add consts in tests
wischli Mar 29, 2023
0839851
tests: apply new messages
wischli Mar 30, 2023
394fd4e
refactor: apply suggestions from soft review
wischli Mar 30, 2023
6a86151
fix: clippy
wischli Mar 30, 2023
3ba9230
tests: fix integration
wischli Mar 31, 2023
9c84f3a
refactor: add LocalCurrencyId storage
wischli Apr 3, 2023
ccc22b0
Merge remote-tracking branch 'origin/main' into feat/connectors-v2-me…
wischli Apr 19, 2023
af67315
feat: add general index mapping for CurrencyId
wischli Apr 19, 2023
492fdb3
tests: fix integration for connectors
wischli Apr 19, 2023
7726985
fix: clippy warning
wischli Apr 19, 2023
93662a1
refactor: apply suggestions from review
wischli Apr 20, 2023
3db1233
refactor: remove unused code
wischli Apr 20, 2023
6cc8863
refactor: apply latest spec changes
wischli Apr 20, 2023
7cacb2b
refactor: remove CollectFor* msgs
wischli Apr 20, 2023
7f60bd4
tests: cover uts for all message cases
wischli Apr 20, 2023
c064dca
refactor: use TryInto instead of custom GeneralCurrencyIndex trait
wischli Apr 20, 2023
dd2356e
refactor: update message fields
wischli Apr 21, 2023
51542c5
feat: complete GeneralCurrencyIndex
wischli Apr 21, 2023
af3ea8e
style: fix indentation
wischli Apr 21, 2023
572fb34
Apply suggestion from code review by @NunoAlexandre
wischli May 2, 2023
4ecddf9
chore: add message docs, fix order
wischli May 2, 2023
2ab8593
Apply suggestion from review by @offerijns
wischli May 2, 2023
618f7c2
Update pallets/connectors/src/message.rs
wischli May 2, 2023
bdbf65d
docs: improve AssetNotFound
wischli May 2, 2023
14b3f09
docs: apply normalized directionality to Message
wischli May 2, 2023
6a06ba4
Merge branch 'main' into feat/connectors-v2-message-poc
wischli May 3, 2023
d774926
refactor: hashed GeneralCurrencyPrefix
wischli May 4, 2023
05dd755
fix: block local domain transfers
wischli May 4, 2023
6b06a9d
refactor: rm unneeded trait bounding
wischli May 4, 2023
e045d0e
Merge remote-tracking branch 'origin/main' into feat/connectors-v2-me…
wischli May 4, 2023
787cc53
fmt: apply comment wrapping
wischli May 4, 2023
2f22dae
fix: remove new extrinsics
wischli May 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions libs/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ pub mod constants {
pub const fn deposit(items: u32, bytes: u32) -> Balance {
items as Balance * 15 * CENTI_CFG + (bytes as Balance) * 6 * CENTI_CFG
}

/// Unhashed 36-bytes prefix for currencies managed by Connectors.
pub const GENERAL_CURRENCY_INDEX_PREFIX: [u8; 36] = *b"CentrifugeGeneralCurrencyIndexPrefix";
}

/// Listing of parachains we integrate with.
Expand Down Expand Up @@ -304,3 +307,21 @@ pub mod parachains {
}
}
}

pub mod connectors {
/// The hashed prefix for currencies managed by Connectors.
pub struct GeneralCurrencyPrefix;

impl sp_core::Get<[u8; 12]> for GeneralCurrencyPrefix {
fn get() -> [u8; 12] {
let hash: [u8; 16] = frame_support::sp_io::hashing::blake2_128(
&crate::constants::GENERAL_CURRENCY_INDEX_PREFIX,
);
let (trimmed, _) = hash.split_at(12);

trimmed
.try_into()
.expect("Should not fail to trim 16-length byte array to length 12")
}
}
}
116 changes: 116 additions & 0 deletions libs/types/src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

use core::marker::PhantomData;

use cfg_primitives::types::{PoolId, TrancheId};
use cfg_traits::TrancheCurrency as TrancheCurrencyT;
use codec::{Decode, Encode, MaxEncodedLen};
pub use orml_asset_registry::AssetMetadata;
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::{traits::Get, DispatchError, TokenError};

use crate::xcm::XcmMetadata;

Expand Down Expand Up @@ -79,6 +82,56 @@ impl From<StakingCurrency> for CurrencyId {
}
}

/// A general index wrapper for a given currency representation which is the
/// concatenation of the generic prefix and the identifier of the respective
/// currency.
pub struct GeneralCurrencyIndex<Index, Prefix> {
pub index: Index,
_phantom: PhantomData<Prefix>,
}

impl<Index, Prefix> TryInto<GeneralCurrencyIndex<Index, Prefix>> for CurrencyId
where
Index: From<u128>,
Prefix: Get<[u8; 12]>,
{
type Error = DispatchError;

fn try_into(self) -> Result<GeneralCurrencyIndex<Index, Prefix>, Self::Error> {
let mut bytes = [0u8; 16];
bytes[..12].copy_from_slice(&Prefix::get());

let currency_bytes: [u8; 4] = match &self {
CurrencyId::ForeignAsset(id32) => Ok(id32.to_be_bytes()),
_ => Err(DispatchError::Token(TokenError::Unsupported)),
}?;
bytes[12..].copy_from_slice(&currency_bytes[..]);

Ok(GeneralCurrencyIndex {
index: u128::from_be_bytes(bytes).into(),
_phantom: Default::default(),
})
}
}

impl<Index, Prefix> TryFrom<GeneralCurrencyIndex<Index, Prefix>> for CurrencyId
where
Index: Into<u128>,
Prefix: Get<[u8; 12]>,
{
type Error = DispatchError;

fn try_from(value: GeneralCurrencyIndex<Index, Prefix>) -> Result<Self, Self::Error> {
let bytes: [u8; 16] = value.index.into().to_be_bytes();
let currency_bytes: [u8; 4] = bytes[12..]
.try_into()
// should never throw but lets be safe
.map_err(|_| DispatchError::Corruption)?;

Ok(CurrencyId::ForeignAsset(u32::from_be_bytes(currency_bytes)))
}
}

/// A Currency that is solely used by tranches.
///
/// We distinguish here between the enum variant CurrencyId::Tranche(PoolId,
Expand Down Expand Up @@ -152,3 +205,66 @@ pub struct CustomMetadata {
/// Whether an asset can be used as a currency to fund Centrifuge Pools.
pub pool_currency: bool,
}

#[cfg(test)]
mod tests {
use frame_support::parameter_types;

use super::*;

const FOREIGN: CurrencyId = CurrencyId::ForeignAsset(1u32);

parameter_types! {
pub const ZeroPrefix: [u8; 12] = [0u8; 12];
pub const NonZeroPrefix: [u8; 12] = *b"TestPrefix12";
}

#[test]
fn zero_prefix_general_index_conversion() {
let general_index: GeneralCurrencyIndex<u128, ZeroPrefix> = FOREIGN.try_into().unwrap();
assert_eq!(general_index.index, 1u128);

// check identity condition on reverse conversion
let reconvert = CurrencyId::try_from(general_index).unwrap();
assert_eq!(reconvert, CurrencyId::ForeignAsset(1u32));
}

#[test]
fn non_zero_prefix_general_index_conversion() {
let general_index: GeneralCurrencyIndex<u128, NonZeroPrefix> = FOREIGN.try_into().unwrap();
assert_eq!(
general_index.index,
112181915321113319688489505016241979393u128
);

// check identity condition on reverse conversion
let reconvert = CurrencyId::try_from(general_index).unwrap();
assert_eq!(reconvert, CurrencyId::ForeignAsset(1u32));
}

#[test]
fn non_foreign_asset_general_index_conversion() {
assert!(
TryInto::<GeneralCurrencyIndex<u128, ZeroPrefix>>::try_into(CurrencyId::Native)
.is_err()
);
assert!(
TryInto::<GeneralCurrencyIndex<u128, ZeroPrefix>>::try_into(CurrencyId::Tranche(
2, [1u8; 16]
))
.is_err()
);
assert!(
TryInto::<GeneralCurrencyIndex<u128, ZeroPrefix>>::try_into(CurrencyId::KSM).is_err()
);
assert!(
TryInto::<GeneralCurrencyIndex<u128, ZeroPrefix>>::try_into(CurrencyId::AUSD).is_err()
);
assert!(
TryInto::<GeneralCurrencyIndex<u128, ZeroPrefix>>::try_into(CurrencyId::Staking(
StakingCurrency::BlockRewards
))
.is_err()
);
}
}
69 changes: 60 additions & 9 deletions pallets/connectors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ pub type TrancheIdOf<T> = <<T as Config>::PoolInspect as PoolInspect<
pub type MessageOf<T> =
Message<Domain, PoolIdOf<T>, TrancheIdOf<T>, <T as Config>::Balance, <T as Config>::Rate>;

pub type CurrencyIdOf<T> = <T as pallet_xcm_transactor::Config>::CurrencyId;
pub type CurrencyIdOf<T> = <T as Config>::CurrencyId;

#[frame_support::pallet]
pub mod pallet {
use cfg_primitives::Moment;
use cfg_traits::{Permissions, PoolInspect, TrancheCurrency};
use cfg_types::{
permissions::{PermissionScope, PoolRole, Role},
tokens::CustomMetadata,
tokens::{CustomMetadata, GeneralCurrencyIndex},
};
use frame_support::{error::BadOrigin, pallet_prelude::*, traits::UnixTime};
use frame_system::pallet_prelude::*;
Expand Down Expand Up @@ -164,6 +164,24 @@ pub mod pallet {
Balance = <Self as Config>::Balance,
CustomMetadata = CustomMetadata,
>;

/// The currency type of transferrable token.
type CurrencyId: Parameter
+ Member
+ Copy
+ MaybeSerializeDeserialize
+ Ord
+ TypeInfo
+ MaxEncodedLen
+ Into<<Self as pallet_xcm_transactor::Config>::CurrencyId>
+ TryInto<
GeneralCurrencyIndex<u128, <Self as Config>::GeneralCurrencyPrefix>,
Error = DispatchError,
>;

/// The prefix for currencies added via Connectors.
#[pallet::constant]
type GeneralCurrencyPrefix: Get<[u8; 12]>;
}

#[pallet::event]
Expand Down Expand Up @@ -199,6 +217,9 @@ pub mod pallet {

#[pallet::error]
pub enum Error<T> {
/// Failed to map the asset to the corresponding Connector's General
/// Index representation
AssetNotFound,
/// A pool could not be found
PoolNotFound,
/// A tranche could not be found
Expand All @@ -221,6 +242,8 @@ pub mod pallet {
InvalidIncomingMessageOrigin,
/// Failed to decode an incoming message
InvalidIncomingMessage,
/// A transfer attempt from the local to the local domain
InvalidTransferDomain,
}

#[pallet::call]
Expand Down Expand Up @@ -279,6 +302,7 @@ pub mod pallet {
origin: OriginFor<T>,
pool_id: PoolIdOf<T>,
tranche_id: TrancheIdOf<T>,
decimals: u8,
domain: Domain,
) -> DispatchResult {
let who = ensure_signed(origin.clone())?;
Expand All @@ -304,6 +328,7 @@ pub mod pallet {
Message::AddTranche {
pool_id,
tranche_id,
decimals,
token_name,
token_symbol,
price,
Expand Down Expand Up @@ -331,7 +356,7 @@ pub mod pallet {

Self::do_send_message(
who,
Message::UpdateTokenPrice {
Message::UpdateTrancheTokenPrice {
pool_id,
tranche_id,
price,
Expand Down Expand Up @@ -386,7 +411,7 @@ pub mod pallet {
pool_id,
tranche_id,
valid_until,
address: domain_address.address(),
member: domain_address.address(),
},
domain_address.domain(),
)?;
Expand All @@ -397,7 +422,7 @@ pub mod pallet {
/// Transfer tranche tokens to a given address
#[pallet::weight(< T as Config >::WeightInfo::transfer())]
#[pallet::call_index(6)]
pub fn transfer(
pub fn transfer_tranche_tokens(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Block local domain!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! 05dd755

origin: OriginFor<T>,
pool_id: PoolIdOf<T>,
tranche_id: TrancheIdOf<T>,
Expand All @@ -406,6 +431,11 @@ pub mod pallet {
) -> DispatchResult {
let who = ensure_signed(origin.clone())?;

// Check that the destination is not the local domain
ensure!(
domain_address.domain() != Domain::Centrifuge,
Error::<T>::InvalidTransferDomain
);
// Check that the destination is a member of this tranche token
ensure!(
T::Permission::has(
Expand All @@ -431,13 +461,17 @@ pub mod pallet {
)?;

Self::do_send_message(
who,
Message::Transfer {
who.clone(),
Message::TransferTrancheTokens {
pool_id,
tranche_id,
amount,
domain: domain_address.domain(),
address: domain_address.address(),
sender: who
.encode()
.try_into()
.map_err(|_| DispatchError::Other("Conversion to 32 bytes failed"))?,
receiver: domain_address.address(),
},
domain_address.domain(),
)?;
Expand Down Expand Up @@ -496,7 +530,7 @@ pub mod pallet {
fee_payer,
// The currency in which we want to pay fees
CurrencyPayment {
currency: Currency::AsCurrencyId(xcm_domain.fee_currency),
currency: Currency::AsCurrencyId(xcm_domain.fee_currency.into()),
fee_amount: None,
},
// The call to be executed in the destination chain
Expand Down Expand Up @@ -558,6 +592,23 @@ pub mod pallet {

encoded
}

/// Returns the `u128` general index of a currency as the concatenation
/// of the configured `GeneralCurrencyPrefix` and its local currency
/// identifier.
///
/// Assumes the currency to be registered in the `AssetRegistry`.
pub fn try_get_general_index(currency: CurrencyIdOf<T>) -> Result<u128, DispatchError> {
ensure!(
T::AssetRegistry::metadata(&currency).is_some(),
Error::<T>::AssetNotFound
);

let general_index: GeneralCurrencyIndex<u128, T::GeneralCurrencyPrefix> =
CurrencyIdOf::<T>::try_into(currency)?;

Ok(general_index.index)
}
}
}

Expand Down