Skip to content

Commit

Permalink
Support reserve deposit for control operations
Browse files Browse the repository at this point in the history
  • Loading branch information
yrong committed Aug 29, 2023
1 parent a0c382c commit 148155c
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 53 deletions.
1 change: 1 addition & 0 deletions parachain/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions parachain/pallets/control/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "eth
[dev-dependencies]
hex = "0.4.1"
hex-literal = { version = "0.4.1" }
pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "master" }

[features]
default = ["std"]
Expand Down
135 changes: 115 additions & 20 deletions parachain/pallets/control/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod benchmarking;
pub mod weights;
pub use weights::*;

use frame_support::traits::fungible::{Inspect, Mutate};
use snowbridge_core::{
outbound::{Command, Message, OperatingMode, OutboundQueue as OutboundQueueTrait, ParaId},
AgentId,
Expand All @@ -31,11 +32,21 @@ pub use pallet::*;

pub const LOG_TARGET: &str = "snowbridge-control";

type BalanceOf<T> =
<<T as pallet::Config>::Token as Inspect<<T as frame_system::Config>::AccountId>>::Balance;

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{log, pallet_prelude::*, traits::EnsureOrigin};
use frame_support::{
log,
pallet_prelude::*,
sp_runtime::traits::AccountIdConversion,
traits::{tokens::Preservation, EnsureOrigin},
PalletId,
};
use frame_system::pallet_prelude::*;
use snowbridge_core::outbound::ControlOperation;

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -71,18 +82,31 @@ pub mod pallet {
/// Location of the relay chain
type RelayLocation: Get<MultiLocation>;

/// Token reserved for control operations
type Token: Mutate<Self::AccountId>;

type WeightInfo: WeightInfo;
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// An Upgrade message was sent to the Gateway
Upgrade { impl_address: H160, impl_code_hash: H256, params_hash: Option<H256> },
Upgrade {
impl_address: H160,
impl_code_hash: H256,
params_hash: Option<H256>,
},
/// An CreateAgent message was sent to the Gateway
CreateAgent { location: Box<MultiLocation>, agent_id: AgentId },
CreateAgent {
location: Box<MultiLocation>,
agent_id: AgentId,
},
/// An CreateChannel message was sent to the Gateway
CreateChannel { para_id: ParaId, agent_id: AgentId },
CreateChannel {
para_id: ParaId,
agent_id: AgentId,
},
/// An UpdateChannel message was sent to the Gateway
UpdateChannel {
para_id: ParaId,
Expand All @@ -92,9 +116,19 @@ pub mod pallet {
reward: u128,
},
/// An SetOperatingMode message was sent to the Gateway
SetOperatingMode { mode: OperatingMode },
SetOperatingMode {
mode: OperatingMode,
},
/// An TransferNativeFromAgent message was sent to the Gateway
TransferNativeFromAgent { agent_id: AgentId, recipient: H160, amount: u128 },
TransferNativeFromAgent {
agent_id: AgentId,
recipient: H160,
amount: u128,
},
FeeUpdated {
operation: ControlOperation,
fee: Option<BalanceOf<T>>,
},
}

#[pallet::error]
Expand All @@ -116,6 +150,10 @@ pub mod pallet {
#[pallet::storage]
pub type Channels<T: Config> = StorageMap<_, Twox64Concat, ParaId, (), OptionQuery>;

#[pallet::storage]
pub type ControlOperationFee<T: Config> =
StorageMap<_, Twox64Concat, ControlOperation, BalanceOf<T>, OptionQuery>;

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Sends a message to the Gateway contract to upgrade itself.
Expand Down Expand Up @@ -160,7 +198,7 @@ pub mod pallet {
pub fn create_agent(origin: OriginFor<T>) -> DispatchResult {
let origin_location: MultiLocation = T::AgentOrigin::ensure_origin(origin)?;

let (agent_id, _, location) = Self::convert_location(origin_location)?;
let (agent_id, para_id, location) = Self::convert_location(origin_location)?;

log::debug!(
target: LOG_TARGET,
Expand All @@ -170,6 +208,11 @@ pub mod pallet {
location
);

Self::reserve_deposit(
para_id,
ControlOperationFee::<T>::get(ControlOperation::CreateAgent),
)?;

// Record the agent id or fail if it has already been created
ensure!(!Agents::<T>::contains_key(agent_id), Error::<T>::AgentAlreadyCreated);

Expand Down Expand Up @@ -197,14 +240,17 @@ pub mod pallet {
pub fn create_channel(origin: OriginFor<T>) -> DispatchResult {
let location: MultiLocation = T::ChannelOrigin::ensure_origin(origin)?;

let (agent_id, some_para_id, _) = Self::convert_location(location)?;
let (agent_id, para_id, _) = Self::convert_location(location)?;

ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::AgentNotExist);

let para_id = some_para_id.ok_or(Error::<T>::LocationToParaIdConversionFailed)?;

ensure!(!Channels::<T>::contains_key(para_id), Error::<T>::ChannelAlreadyCreated);

Self::reserve_deposit(
para_id,
ControlOperationFee::<T>::get(ControlOperation::CreateChannel),
)?;

Channels::<T>::insert(para_id, ());

let message = Message {
Expand All @@ -231,14 +277,17 @@ pub mod pallet {
) -> DispatchResult {
let location: MultiLocation = T::ChannelOrigin::ensure_origin(origin)?;

let (agent_id, some_para_id, _) = Self::convert_location(location)?;
let (agent_id, para_id, _) = Self::convert_location(location)?;

ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::AgentNotExist);

let para_id = some_para_id.ok_or(Error::<T>::LocationToParaIdConversionFailed)?;

ensure!(Channels::<T>::contains_key(para_id), Error::<T>::ChannelNotExist);

Self::reserve_deposit(
para_id,
ControlOperationFee::<T>::get(ControlOperation::UpdateChannel),
)?;

let message = Message {
origin: T::OwnParaId::get(),
command: Command::UpdateChannel { para_id, mode, fee, reward },
Expand Down Expand Up @@ -281,10 +330,15 @@ pub mod pallet {
) -> DispatchResult {
let location: MultiLocation = T::AgentOrigin::ensure_origin(origin)?;

let (agent_id, _, _) = Self::convert_location(location)?;
let (agent_id, para_id, _) = Self::convert_location(location)?;

ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::AgentNotExist);

Self::reserve_deposit(
para_id,
ControlOperationFee::<T>::get(ControlOperation::TransferNativeFromAgent),
)?;

let message = Message {
origin: T::OwnParaId::get(),
command: Command::TransferNativeFromAgent { agent_id, recipient, amount },
Expand All @@ -299,6 +353,27 @@ pub mod pallet {

Ok(())
}

/// - `update`: (ControlOperation, Fee).
#[pallet::call_index(6)]
#[pallet::weight(T::WeightInfo::update_operation_fee())]
pub fn update_operation_fee(
origin: OriginFor<T>,
operation: ControlOperation,
update_fee: Option<BalanceOf<T>>,
) -> DispatchResult {
ensure_root(origin)?;

ControlOperationFee::<T>::mutate(&operation, |fee| {
*fee = update_fee;
Self::deposit_event(Event::<T>::FeeUpdated {
operation: operation.clone(),
fee: update_fee,
});
});

Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand All @@ -311,25 +386,45 @@ pub mod pallet {

pub fn convert_location(
mut location: MultiLocation,
) -> Result<(H256, Option<ParaId>, MultiLocation), DispatchError> {
) -> Result<(H256, ParaId, MultiLocation), DispatchError> {
// Normalize all locations relative to the relay chain.
let relay_location = T::RelayLocation::get();
location
.reanchor(&relay_location, T::UniversalLocation::get())
.map_err(|_| Error::<T>::LocationReanchorFailed)?;

// Only allow Parachain as origin location
let para_id = match location {
MultiLocation { parents: 0, interior: X1(Parachain(index)) } =>
Some((index).into()),
let para_id = match location.interior.first() {
Some(Parachain(index)) => Some((*index).into()),
_ => None,
};
}
.ok_or(Error::<T>::LocationToParaIdConversionFailed)?;

// Hash the location to produce an agent id
let agent_id = T::AgentHashedDescription::convert_location(&location)
.ok_or(Error::<T>::LocationToAgentIdConversionFailed)?;

Ok((agent_id, para_id, location))
}

pub fn account_id() -> T::AccountId {
PalletId(*b"snow/ctl").into_account_truncating()
}

pub fn reserve_deposit(
para_id: ParaId,
reserve_deposit: Option<BalanceOf<T>>,
) -> DispatchResult {
if reserve_deposit.is_some() {
// Transfer reserve deposit from the sovereign account of the destination parachain
// to internal pallet account
T::Token::transfer(
&para_id.into_account_truncating(),
&Self::account_id(),
reserve_deposit.unwrap(),
Preservation::Preserve,
)?;
}
Ok(())
}
}
}
20 changes: 19 additions & 1 deletion parachain/pallets/control/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ frame_support::construct_runtime!(
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system,
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
EthereumControl: snowbridge_control,
}
);
Expand All @@ -54,7 +55,7 @@ impl frame_system::Config for Test {
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
Expand All @@ -63,6 +64,22 @@ impl frame_system::Config for Test {
type MaxConsumers = frame_support::traits::ConstU32<16>;
}

impl pallet_balances::Config for Test {
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = u64;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ConstU64<1>;
type AccountStore = System;
type WeightInfo = ();
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeHoldReason = ();
type MaxHolds = ();
}

parameter_types! {
pub const OwnParaId: ParaId = ParaId::new(1013);
pub const MaxUpgradeDataSize: u32 = 1024;
Expand Down Expand Up @@ -196,6 +213,7 @@ impl snowbridge_control::Config for Test {
type UniversalLocation = UniversalLocation;
type RelayLocation = RelayLocation;
type AgentHashedDescription = HashedDescription<H256, DescribeFamily<DescribeAllTerminal>>;
type Token = Balances;
type WeightInfo = ();
}

Expand Down
14 changes: 0 additions & 14 deletions parachain/pallets/control/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,6 @@ fn create_channel_with_sibling_chain_origin_yields_success() {
});
}

#[test]
fn create_channel_with_sibling_chain_pallet_as_origin_yields_location_conversion_failed() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([6; 32]));

assert_ok!(EthereumControl::create_agent(origin.clone()));

frame_support::assert_noop!(
EthereumControl::create_channel(origin),
Error::<Test>::LocationToParaIdConversionFailed
);
});
}

#[test]
fn create_channel_already_exist_yields_failed() {
new_test_ext().execute_with(|| {
Expand Down
7 changes: 7 additions & 0 deletions parachain/pallets/control/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub trait WeightInfo {
fn update_channel() -> Weight;
fn set_operating_mode() -> Weight;
fn transfer_native_from_agent() -> Weight;
fn update_operation_fee() -> Weight;
}

// For backwards compatibility and tests
Expand Down Expand Up @@ -83,4 +84,10 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4))
}
fn update_operation_fee() -> Weight {
Weight::from_parts(35_000_000, 0)
.saturating_add(Weight::from_parts(0, 3517))
.saturating_add(RocksDbWeight::get().reads(5))
.saturating_add(RocksDbWeight::get().writes(4))
}
}
10 changes: 9 additions & 1 deletion parachain/primitives/core/src/outbound.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use codec::{Decode, Encode};
use codec::{Decode, Encode, MaxEncodedLen};
pub use polkadot_parachain::primitives::Id as ParaId;
use scale_info::TypeInfo;
use sp_core::{RuntimeDebug, H160, H256, U256};
Expand Down Expand Up @@ -226,3 +226,11 @@ impl AgentExecuteCommand {
}
}
}

#[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum ControlOperation {
CreateAgent,
CreateChannel,
UpdateChannel,
TransferNativeFromAgent,
}
3 changes: 2 additions & 1 deletion web/packages/test/scripts/configure-bridgehub.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ wait_beacon_chain_ready()
fund_accounts()
{
echo "Funding substrate accounts"
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $statemine_sovereign_account
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $assethub_sovereign_account
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $template_sovereign_account
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $beacon_relayer_pub_key
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $execution_relayer_pub_key
transfer_balance $relaychain_ws_url "//Charlie" 1000 1000000000000000 $gateway_contract_sovereign_account
Expand Down
Loading

0 comments on commit 148155c

Please sign in to comment.