Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ParthDesai committed Nov 18, 2024
1 parent 72f296c commit 63f4554
Show file tree
Hide file tree
Showing 11 changed files with 1,419 additions and 352 deletions.
1,040 changes: 697 additions & 343 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -258,12 +258,16 @@ xcm-runtime-apis = { git = "https://github.com/moondance-labs/polkadot-sdk", bra


# Bridges (wasm)
alloy-primitives = { version = "0.4.2", default-features = false }
alloy-sol-types = { version = "0.4.2", default-features = false }
milagro-bls = { package = "snowbridge-milagro-bls", version = "1.5.4", default-features = false }
snowbridge-beacon-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-core = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-pallet-ethereum-client = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-pallet-inbound-queue = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-pallet-outbound-queue = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-pallet-system = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }
snowbridge-router-primitives = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407", default-features = false }

# Polkadot (client)
polkadot-cli = { git = "https://github.com/moondance-labs/polkadot-sdk", branch = "tanssi-polkadot-stable2407" }
Expand Down
8 changes: 8 additions & 0 deletions solo-chains/runtime/dancelight/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,14 @@ pallet-migrations = { workspace = true }
snowbridge-beacon-primitives = { workspace = true }
snowbridge-core = { workspace = true }
snowbridge-pallet-ethereum-client = { workspace = true }
snowbridge-pallet-inbound-queue = { workspace = true }
snowbridge-pallet-outbound-queue = { workspace = true }
snowbridge-pallet-system = { workspace = true }
snowbridge-router-primitives = { workspace = true }
tp-bridge = { workspace = true }

[dev-dependencies]
alloy-sol-types = { workspace = true, default-features = true }
finality-grandpa = { workspace = true, default-features = true, features = [ "derive-codec" ] }
keyring = { workspace = true }
milagro-bls = { workspace = true, features = [ "std" ] }
Expand Down Expand Up @@ -265,8 +268,10 @@ std = [
"snowbridge-core/std",
"snowbridge-pallet-ethereum-client/fuzzing",
"snowbridge-pallet-ethereum-client/std",
"snowbridge-pallet-inbound-queue/std",
"snowbridge-pallet-outbound-queue/std",
"snowbridge-pallet-system/std",
"snowbridge-router-primitives/std",
"sp-api/std",
"sp-application-crypto/std",
"sp-arithmetic/std",
Expand Down Expand Up @@ -356,8 +361,10 @@ runtime-benchmarks = [
"runtime-parachains/runtime-benchmarks",
"snowbridge-core/runtime-benchmarks",
"snowbridge-pallet-ethereum-client/runtime-benchmarks",
"snowbridge-pallet-inbound-queue/runtime-benchmarks",
"snowbridge-pallet-outbound-queue/runtime-benchmarks",
"snowbridge-pallet-system/runtime-benchmarks",
"snowbridge-router-primitives/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"tanssi-runtime-common/runtime-benchmarks",
Expand Down Expand Up @@ -428,6 +435,7 @@ try-runtime = [
"runtime-common/try-runtime",
"runtime-parachains/try-runtime",
"snowbridge-pallet-ethereum-client/try-runtime",
"snowbridge-pallet-inbound-queue/try-runtime",
"snowbridge-pallet-outbound-queue/try-runtime",
"snowbridge-pallet-system/try-runtime",
"sp-runtime/try-runtime",
Expand Down
79 changes: 73 additions & 6 deletions solo-chains/runtime/dancelight/src/bridge_to_ethereum_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,32 @@

pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32;

use crate::symbiotic_message_processor::SymbioticMessageProcessor;
use frame_support::weights::ConstantMultiplier;
use parity_scale_codec::Encode;
use snowbridge_router_primitives::inbound::{
ConvertMessage, ConvertMessageError, VersionedXcmMessage,
};
use sp_core::H160;
use sp_core::{ConstU32, ConstU8};
use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash};
use {
crate::{
parameter_types, weights, AggregateMessageOrigin, Balance, Balances, EthereumOutboundQueue,
EthereumSystem, FixedU128, GetAggregateMessageOrigin, Keccak256, MessageQueue, Runtime,
RuntimeEvent, TreasuryAccount, WeightToFee, UNITS,
parameter_types, weights, xcm_config, AccountId, AggregateMessageOrigin, Balance, Balances,
EthereumBeaconClient, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem,
FixedU128, GetAggregateMessageOrigin, Keccak256, MessageQueue, Runtime, RuntimeEvent,
TransactionByteFee, TreasuryAccount, WeightToFee, UNITS,
},
pallet_xcm::EnsureXcm,
snowbridge_beacon_primitives::{Fork, ForkVersions},
snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards},
sp_core::{ConstU32, ConstU8},
};

// Ethereum Bridge
parameter_types! {
pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39"));
}

parameter_types! {
pub Parameters: PricingParameters<u128> = PricingParameters {
exchange_rate: FixedU128::from_rational(1, 400),
Expand Down Expand Up @@ -140,8 +154,7 @@ impl snowbridge_pallet_system::Config for Runtime {
#[cfg(feature = "runtime-benchmarks")]
type Helper = benchmark_helper::EthSystemBenchHelper;
type DefaultPricingParameters = Parameters;
type InboundDeliveryCost = ();
//type InboundDeliveryCost = EthereumInboundQueue;
type InboundDeliveryCost = EthereumInboundQueue;
}

#[cfg(feature = "runtime-benchmarks")]
Expand All @@ -155,4 +168,58 @@ mod benchmark_helper {
RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location))
}
}

impl snowbridge_pallet_system::BenchmarkHelper<RuntimeOrigin> for () {
fn make_xcm_origin(location: Location) -> RuntimeOrigin {
RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location))
}
}
}

pub struct DoNothingRouter;
impl SendXcm for DoNothingRouter {
type Ticket = Xcm<()>;

fn validate(
_dest: &mut Option<Location>,
xcm: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
Ok((xcm.clone().unwrap(), Assets::new()))
}
fn deliver(xcm: Xcm<()>) -> Result<XcmHash, SendError> {
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
Ok(hash)
}
}

pub struct DoNothingConvertMessage;

impl ConvertMessage for DoNothingConvertMessage {
type Balance = Balance;
type AccountId = AccountId;

fn convert(
_message: VersionedXcmMessage,
) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> {
Err(ConvertMessageError::UnsupportedVersion)
}
}

impl snowbridge_pallet_inbound_queue::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Verifier = EthereumBeaconClient;
type Token = Balances;
type XcmSender = DoNothingRouter;
type GatewayAddress = EthereumGatewayAddress;
type MessageConverter = DoNothingConvertMessage;
type ChannelLookup = EthereumSystem;
type PricingParameters = EthereumSystem;
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
type Helper = Runtime;
type WeightToFee = WeightToFee;
type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
type MaxMessageSize = ConstU32<2048>;
type AssetTransactor = <xcm_config::XcmConfig as xcm_executor::Config>::AssetTransactor;
type MessageProcessor = (SymbioticMessageProcessor<Runtime>,);
}
5 changes: 2 additions & 3 deletions solo-chains/runtime/dancelight/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ use dancelight_runtime_constants::{currency::*, fee::*, time::*};
pub mod xcm_config;

pub mod bridge_to_ethereum_config;
pub mod symbiotic_message_processor;

// Weights
mod weights;
Expand Down Expand Up @@ -1738,10 +1739,8 @@ construct_runtime! {

// Bridging stuff
// https://github.com/paritytech/polkadot-sdk/blob/2ae79be8e028a995b850621ee55f46c041eceefe/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs#L560C1-L560C64
//EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80,
EthereumInboundQueue: snowbridge_pallet_inbound_queue = 91,
EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 101,
// TODO: already exists, at index 243
//EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82,
EthereumSystem: snowbridge_pallet_system = 103,

// Migration stuff
Expand Down
73 changes: 73 additions & 0 deletions solo-chains/runtime/dancelight/src/symbiotic_message_processor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use frame_support::pallet_prelude::*;
use parity_scale_codec::DecodeAll;
use snowbridge_core::Channel;
use snowbridge_router_primitives::inbound::envelope::Envelope;
use snowbridge_router_primitives::inbound::MessageProcessor;
use sp_runtime::DispatchError;
use sp_std::vec::Vec;

/// Magic bytes are added in every payload intended for this processor to make sure
/// that we are the intended recipient of the message. Reason being scale encoding is not type aware.
/// So a same set of bytes can be decoded for two different data structures if their
/// total size is same. Magic bytes can be checked after decoding to make sure that the sender
/// indeed send a message intended for this processor.
pub const MAGIC_BYTES: [u8; 4] = [112, 21, 0, 56];

#[derive(Encode, Decode)]
pub struct Payload<T>
where
T: pallet_external_validators::Config,
{
pub magic_bytes: [u8; 4],
pub message: Message<T>,
}

#[derive(Encode, Decode)]
pub enum Message<T>
where
T: pallet_external_validators::Config,
{
V1(Command<T>),
}

#[derive(Encode, Decode)]
pub enum Command<T>
where
T: pallet_external_validators::Config,
{
ReceiveValidators {
validators: Vec<<T as pallet_external_validators::Config>::ValidatorId>,
},
}

pub struct SymbioticMessageProcessor<T>(PhantomData<T>);

impl<T> MessageProcessor for SymbioticMessageProcessor<T>
where
T: pallet_external_validators::Config,
{
fn can_process_message(_channel: &Channel, envelope: &Envelope) -> bool {
let decode_result = Payload::<T>::decode_all(&mut envelope.payload.as_slice());
if let Ok(payload) = decode_result {
payload.magic_bytes == MAGIC_BYTES
} else {
false
}
}

fn process_message(_channel: Channel, envelope: Envelope) -> Result<(), DispatchError> {
let decode_result = Payload::<T>::decode_all(&mut envelope.payload.as_slice());
let message = if let Ok(payload) = decode_result {
payload.message
} else {
return Err(DispatchError::Other("unable to parse the payload"));
};

match message {
Message::V1(Command::ReceiveValidators { validators }) => {
pallet_external_validators::Pallet::<T>::set_external_validators(validators)?;
Ok(())
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::symbiotic_message_processor::{Command, Payload, MAGIC_BYTES};
use crate::tests::inbound_queue_tests::mock::{
mock_ext, AccountId, ExternalValidators as MockExternalValidators, InboundQueue,
Test as TestRuntime, MOCK_CHANNEL_ID,
};
use alloy_sol_types::SolEvent;
use frame_system::pallet_prelude::OriginFor;
use keyring::AccountKeyring;
use parity_scale_codec::Encode;
use snowbridge_beacon_primitives::types::deneb;
use snowbridge_beacon_primitives::{ExecutionProof, VersionedExecutionPayloadHeader};
use snowbridge_core::inbound::{Log, Message, Proof};
use snowbridge_router_primitives::inbound::envelope::OutboundMessageAccepted;
use sp_core::H256;
use sp_runtime::DispatchError;

#[test]
fn test_inbound_queue_message_passing() {
mock_ext().execute_with(|| {
let current_nonce = 1;

let dummy_proof = Proof { receipt_proof: (vec![], vec![]), execution_proof: ExecutionProof {
header: Default::default(),
ancestry_proof: None,
execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader {
parent_hash: Default::default(),
fee_recipient: Default::default(),
state_root: Default::default(),
receipts_root: Default::default(),
logs_bloom: vec![],
prev_randao: Default::default(),
block_number: 0,
gas_limit: 0,
gas_used: 0,
timestamp: 0,
extra_data: vec![],
base_fee_per_gas: Default::default(),
block_hash: Default::default(),
transactions_root: Default::default(),
withdrawals_root: Default::default(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
execution_branch: vec![],
} };

let event_with_empty_payload = OutboundMessageAccepted {
channel_id: MOCK_CHANNEL_ID.into(),
nonce: current_nonce,
message_id: Default::default(),
payload: vec![],
};

assert_eq!(InboundQueue::submit(OriginFor::<TestRuntime>::signed(AccountId::new([0; 32])), Message {
event_log: Log {
address: <TestRuntime as snowbridge_pallet_inbound_queue::Config>::GatewayAddress::get(),
topics: event_with_empty_payload.encode_topics().into_iter().map(|word| H256::from(word.0.0)).collect(),
data: event_with_empty_payload.encode_data(),
},
proof: dummy_proof.clone(),
}), Err(DispatchError::Other("No handler for message found")));

assert_eq!(MockExternalValidators::validators(), MockExternalValidators::whitelisted_validators());

let payload_validators = vec![
AccountKeyring::Charlie.to_account_id(),
AccountKeyring::Ferdie.to_account_id(),
AccountKeyring::BobStash.to_account_id()
];

let payload = Payload {
magic_bytes: MAGIC_BYTES,
message: crate::symbiotic_message_processor::Message::V1(Command::<TestRuntime>::ReceiveValidators {
validators: payload_validators.clone()
}),
};

let event_with_valid_payload = OutboundMessageAccepted {
channel_id: MOCK_CHANNEL_ID.into(),
nonce: current_nonce,
message_id: Default::default(),
payload: payload.encode(),
};

assert_eq!(InboundQueue::submit(OriginFor::<TestRuntime>::signed(AccountId::new([0; 32])), Message {
event_log: Log {
address: <TestRuntime as snowbridge_pallet_inbound_queue::Config>::GatewayAddress::get(),
topics: event_with_valid_payload.encode_topics().into_iter().map(|word| H256::from(word.0.0)).collect(),
data: event_with_valid_payload.encode_data(),
},
proof: dummy_proof.clone(),
}), Ok(()));


let expected_validators = [MockExternalValidators::whitelisted_validators(), payload_validators].concat();
assert_eq!(MockExternalValidators::validators(), expected_validators);
});
}
Loading

0 comments on commit 63f4554

Please sign in to comment.