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

lp-gateway: Emit more events when processing an incoming message #1550

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 19 additions & 1 deletion pallets/liquidity-pools-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
const BYTES_U32: usize = 4;
const BYTES_ACCOUNT_20: usize = 20;

use frame_support::traits::OriginTrait;

Check warning on line 60 in pallets/liquidity-pools-gateway/src/lib.rs

View workflow job for this annotation

GitHub Actions / docs

unused import: `frame_support::traits::OriginTrait`

use super::*;
use crate::RelayerMessageDecodingError::{
MalformedMessage, MalformedSourceAddress, MalformedSourceAddressLength,
Expand Down Expand Up @@ -148,6 +150,12 @@
message: Vec<u8>,
domain: Domain,
},

/// An incoming message has been received.
IncomingMessageReceived { message: Vec<u8> },

/// An invalid origin was used for an incoming message.
InvalidIncomingMessageOrigin,
}

/// Storage for domain routers.
Expand Down Expand Up @@ -328,7 +336,17 @@
origin: OriginFor<T>,
msg: BoundedVec<u8, T::MaxIncomingMessageSize>,
) -> DispatchResult {
let (domain_address, incoming_msg) = match T::LocalEVMOrigin::ensure_origin(origin)? {
Self::deposit_event(Event::IncomingMessageReceived {
message: msg.to_vec(),
});

let origin = T::LocalEVMOrigin::ensure_origin(origin).map_err(|e| {
Self::deposit_event(Event::InvalidIncomingMessageOrigin);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We will never see that. Upon error the state will be discarded. We would need to make this method non_transactional in order to see events of failures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah ok, but making it non-transactional would not be OK given that we're calling the LP pallet right?

I can also remove this, the one thing that is important here would be the test that confirms that an incoming xcm call is executed accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah ok, but making it non-transactional would not be OK given that we're calling the LP pallet right?

If we removed the transactional component, we would have to ensure no storage is mutated until errors cannot occur anymore. However AFAIK, we can have transactional sub-scope within process_msg such as submit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The inbound queue implemented by the LP pallet is marked as transactional, does that mean that we can mark process_msg as non-transactional then?

https://github.com/centrifuge/centrifuge-chain/blob/main/pallets/liquidity-pools/src/lib.rs#L937

Copy link
Contributor

@wischli wischli Sep 19, 2023

Choose a reason for hiding this comment

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

The inbound queue implemented by the LP pallet is marked as transactional, does that mean that we can mark process_msg as non-transactional then?

Yes, that would be possible. However, we should keep extrinsics transactional and rather check, whether we can remove that layer from the InboundQueue::submit impl of pallet-liquidity-pools. I had to introduce it in #1292 because otherwise the inbound handlers mutated storage even if an error was thrown. If we are 100% sure that InboundQueue::submit will be called by a transactional call, we can remove it from submit.

I had to ensure myself via some local tests: If a transactional call c executes nested non-transactional calls nested_c which mutate storage, then all storage mutations of c including nested_c are still rolled back if any error is thrown by c.

Transactional layers come with minor overhead:

  • 50 ns for the layer itself
  • ~146 ns writing storage item from transactional layer to db which is only ~0.15% overhead compared to weight for one RocksDb write


e
})?;

let (domain_address, incoming_msg) = match origin {
GatewayOrigin::Domain(domain_address) => {
Pallet::<T>::validate(domain_address, msg)?
}
Expand Down
31 changes: 31 additions & 0 deletions runtime/common/src/xcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use cfg_types::{
EVMChainId, ParaId,
};
use frame_support::sp_std::marker::PhantomData;
use sp_core::H160;
use sp_runtime::traits::Convert;
use xcm::v3::{
Junction::{AccountId32, AccountKey20, GeneralKey, Parachain},
Expand Down Expand Up @@ -114,6 +115,36 @@ where
Err(location)
}
}
// IMPORTANT - This only applies in our integration test environment since the
// `Moonbeam` parachain that we setup there is using the same AccountId as we do on
// Centrifuge, which is 32 bytes instead of 20.
//
// !!! REMOVE BEFORE MERGING !!!
MultiLocation {
parents: 1,
interior: X2(Parachain(para), AccountId32 { network: _, id }),
} => {
let evm_id = ParaAsEvmChain::try_convert(para).map_err(|_| location)?;

let domain_address = DomainAddress::EVM(
evm_id,
H160::from_slice(&id.as_ref()[0..20]).to_fixed_bytes(),
);

if pallet_liquidity_pools_gateway::Pallet::<Runtime>::relayer(
Domain::EVM(evm_id),
&domain_address,
)
.is_some()
{
Ok(pallet_liquidity_pools_gateway::GatewayOrigin::AxelarRelay(
domain_address,
)
.into())
} else {
Err(location)
}
}
_ => Err(location),
},
_ => Err(location),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub const TEST_DOMAIN: Domain = Domain::EVM(1284);

/// A PARA ID used for a sibling parachain emulating Moonbeam.
/// It must be one that doesn't collide with any other in use.
pub const PARA_ID_MOONBEAM: u32 = 2023;
pub const PARA_ID_MOONBEAM: u32 = 1000;

pub struct ExtBuilder {
balances: Vec<(AccountId, CurrencyId, Balance)>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ decl_test_network! {
// Be sure to use `parachains::polkadot::centrifuge::ID`
(2031, Development),
// Be sure to use `PARA_ID_MOONBEAM`
(2023, Moonbeam),
(1000, Moonbeam),
],
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,45 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

use cfg_primitives::{constants::currency_decimals, parachains, Balance};
use cfg_primitives::{constants::currency_decimals, parachains, Balance, PoolId, TrancheId};
use cfg_traits::{liquidity_pools::Codec, TryConvert};
use cfg_types::{
domain_address::{Domain, DomainAddress},
fixed_point::Rate,
tokens::{CrossChainTransferability, CurrencyId, CustomMetadata},
xcm::XcmMetadata,
};
use development_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens};
use frame_support::assert_ok;
use cfg_utils::vec_to_fixed_array;
use codec::Encode;
use development_runtime::{
Balances, LiquidityPoolsGateway, LocationToAccountId, OrmlAssetRegistry, OrmlTokens,
Runtime as DevelopmentRuntime, RuntimeCall as DevelopmentRuntimeCall, RuntimeOrigin, System,
XTokens, XcmTransactor,
};
use frame_support::{assert_ok, traits::fungible::Mutate};
use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency};
use pallet_liquidity_pools::Message;
use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights};
use runtime_common::{
account_conversion::AccountConverter,
xcm::general_key,
xcm_fees::{default_per_second, ksm_per_second},
};
use sp_core::{bounded::BoundedVec, ConstU32, H160};
use sp_runtime::traits::BadOrigin;
use xcm::{
latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId, WeightLimit},
VersionedMultiLocation,
v3::{OriginKind, Weight},
VersionedMultiLocation, VersionedXcm,
};
use xcm_emulator::TestExt;

use crate::liquidity_pools::pallet::{
development::{
setup::{centrifuge_account, cfg, moonbeam_account, ALICE, BOB, CHARLIE, PARA_ID_MOONBEAM},
setup::{
centrifuge_account, cfg, dollar, moonbeam_account, ALICE, BOB, CHARLIE,
PARA_ID_MOONBEAM,
},
test_net::{Development, Moonbeam, RelayChain, TestNet},
tests::register_ausd,
},
Expand All @@ -61,6 +78,89 @@ which would go unnoticed and untreated otherwise.

*/

#[test]
fn incoming_xcm() {
TestNet::reset();

let test_address = H160::from_low_u64_be(345654);
let msg = Message::<Domain, PoolId, TrancheId, Balance, Rate>::AddCurrency {
currency: 0,
evm_address: test_address.0,
};

let lp_gateway_msg = BoundedVec::<
u8,
<DevelopmentRuntime as pallet_liquidity_pools_gateway::Config>::MaxIncomingMessageSize,
>::try_from(msg.serialize())
.expect("msg should convert to BoundedVec");

let call = DevelopmentRuntimeCall::LiquidityPoolsGateway(
pallet_liquidity_pools_gateway::Call::process_msg {
msg: lp_gateway_msg,
},
);

let cfg_in_sibling = CurrencyId::ForeignAsset(12);

transfer_cfg_to_sibling();

let dest = MultiLocation::new(1, X1(Parachain(parachains::polkadot::centrifuge::ID)));

Development::execute_with(|| {
assert_ok!(LiquidityPoolsGateway::add_relayer(
RuntimeOrigin::root(),
DomainAddress::EVM(
1282,
H160::from_slice(&BOB.as_ref()[0..20]).to_fixed_bytes()
)
));
});

Moonbeam::execute_with(|| {
XcmTransactor::set_transact_info(
RuntimeOrigin::root(),
Box::new(dest.into()),
Default::default(),
Default::default(),
Default::default(),
)?;

XcmTransactor::set_fee_per_second(
RuntimeOrigin::root(),
Box::new(VersionedMultiLocation::V3(MultiLocation::new(
1,
X2(
Parachain(parachains::polkadot::centrifuge::ID),
general_key(parachains::polkadot::centrifuge::CFG_KEY),
),
))),
100,
)
});

Moonbeam::execute_with(|| {
assert_ok!(XcmTransactor::transact_through_signed(
RuntimeOrigin::signed(BOB.into()),
Box::new(dest.into()),
CurrencyPayment {
currency: Currency::AsCurrencyId(cfg_in_sibling),
fee_amount: Some(155548480000000000),
},
call.encode().into(),
TransactWeights {
transact_required_weight_at_most: Weight::from_all(12530000000),
overall_weight: Some(Weight::from_all(15530000000)),
},
));
});

Development::execute_with(|| {
let events = System::events();

assert!(events.len() > 0)
});
}

#[test]
fn transfer_cfg_to_sibling() {
TestNet::reset();
Expand Down Expand Up @@ -89,7 +189,22 @@ fn transfer_cfg_to_sibling() {
},
};

let location = MultiLocation {
parents: 1,
interior: X2(
Parachain(PARA_ID_MOONBEAM),
AccountId32 {
network: None,
id: BOB.into(),
},
),
};

let bob_acc =
AccountConverter::<DevelopmentRuntime, LocationToAccountId>::try_convert(location).unwrap();

Development::execute_with(|| {
assert_ok!(Balances::mint_into(&bob_acc.into(), 1000 * dollar(18)));
assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance);
assert_eq!(Balances::free_balance(&moonbeam_account()), 0);

Expand Down
Loading