// Copyright 2019-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . use crate::Config; use bp_message_lane::{ source_chain::{ LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, Sender, TargetHeaderChain, }, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; use bp_runtime::Size; use codec::{Decode, Encode}; use frame_support::{parameter_types, weights::Weight}; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, traits::{BlakeTwo256, IdentityLookup}, Perbill, }; use std::collections::BTreeMap; pub type AccountId = u64; pub type Balance = u64; #[derive(Decode, Encode, Clone, Debug, PartialEq, Eq)] pub struct TestPayload(pub u64, pub Weight); pub type TestMessageFee = u64; pub type TestRelayer = u64; pub struct AccountIdConverter; impl sp_runtime::traits::Convert for AccountIdConverter { fn convert(hash: H256) -> AccountId { hash.to_low_u64_ne() } } type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; use crate as pallet_message_lane; frame_support::construct_runtime! { pub enum TestRuntime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Module, Call, Config, Storage, Event}, Balances: pallet_balances::{Module, Call, Event}, MessageLane: pallet_message_lane::{Module, Call, Event}, } } parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Config for TestRuntime { type Origin = Origin; type Index = u64; type Call = Call; type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Header = SubstrateHeader; type Event = Event; type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type BaseCallFilter = (); type SystemWeightInfo = (); type BlockWeights = (); type BlockLength = (); type DbWeight = (); type SS58Prefix = (); } parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Config for TestRuntime { type MaxLocks = (); type Balance = Balance; type DustRemoval = (); type Event = Event; type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Module; type WeightInfo = (); } parameter_types! { pub const MaxMessagesToPruneAtOnce: u64 = 10; pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; } impl Config for TestRuntime { type Event = Event; type WeightInfo = (); type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type OutboundPayload = TestPayload; type OutboundMessageFee = TestMessageFee; type InboundPayload = TestPayload; type InboundMessageFee = TestMessageFee; type InboundRelayer = TestRelayer; type AccountIdConverter = AccountIdConverter; type TargetHeaderChain = TestTargetHeaderChain; type LaneMessageVerifier = TestLaneMessageVerifier; type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; type SourceHeaderChain = TestSourceHeaderChain; type MessageDispatch = TestMessageDispatch; } impl Size for TestPayload { fn size_hint(&self) -> u32 { 16 } } /// Account that has balance to use in tests. pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; /// Account id of test relayer. pub const TEST_RELAYER_A: AccountId = 100; /// Account id of additional test relayer - B. pub const TEST_RELAYER_B: AccountId = 101; /// Account id of additional test relayer - C. pub const TEST_RELAYER_C: AccountId = 102; /// Error that is returned by all test implementations. pub const TEST_ERROR: &str = "Test error"; /// Lane that we're using in tests. pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = TestPayload(0, 50); /// Payload that is rejected by `TestTargetHeaderChain`. pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = TestPayload(1, 50); /// Vec of proved messages, grouped by lane. pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages>)>; /// Test messages proof. #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq)] pub struct TestMessagesProof { pub result: Result, } impl Size for TestMessagesProof { fn size_hint(&self) -> u32 { 0 } } impl From>, ()>> for TestMessagesProof { fn from(result: Result>, ()>) -> Self { Self { result: result.map(|messages| { let mut messages_by_lane: BTreeMap>> = BTreeMap::new(); for message in messages { messages_by_lane .entry(message.key.lane_id) .or_default() .messages .push(message); } messages_by_lane.into_iter().collect() }), } } } /// Messages delivery proof used in tests. #[derive(Debug, Encode, Decode, Eq, Clone, PartialEq)] pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); impl Size for TestMessagesDeliveryProof { fn size_hint(&self) -> u32 { 0 } } /// Target header chain that is used in tests. #[derive(Debug, Default)] pub struct TestTargetHeaderChain; impl TargetHeaderChain for TestTargetHeaderChain { type Error = &'static str; type MessagesDeliveryProof = TestMessagesDeliveryProof; fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> { if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { Err(TEST_ERROR) } else { Ok(()) } } fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { proof.0.map_err(|_| TEST_ERROR) } } /// Lane message verifier that is used in tests. #[derive(Debug, Default)] pub struct TestLaneMessageVerifier; impl LaneMessageVerifier for TestLaneMessageVerifier { type Error = &'static str; fn verify_message( _submitter: &Sender, delivery_and_dispatch_fee: &TestMessageFee, _lane: &LaneId, _lane_outbound_data: &OutboundLaneData, _payload: &TestPayload, ) -> Result<(), Self::Error> { if *delivery_and_dispatch_fee != 0 { Ok(()) } else { Err(TEST_ERROR) } } } /// Message fee payment system that is used in tests. #[derive(Debug, Default)] pub struct TestMessageDeliveryAndDispatchPayment; impl TestMessageDeliveryAndDispatchPayment { /// Reject all payments. pub fn reject_payments() { frame_support::storage::unhashed::put(b":reject-message-fee:", &true); } /// Returns true if given fee has been paid by given submitter. pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool { frame_support::storage::unhashed::get(b":message-fee:") == Some((Sender::Signed(submitter), fee)) } /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is /// cleared after the call. pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { let key = (b":relayer-reward:", relayer, fee).encode(); frame_support::storage::unhashed::take::(&key).is_some() } } impl MessageDeliveryAndDispatchPayment for TestMessageDeliveryAndDispatchPayment { type Error = &'static str; fn pay_delivery_and_dispatch_fee( submitter: &Sender, fee: &TestMessageFee, _relayer_fund_account: &AccountId, ) -> Result<(), Self::Error> { if frame_support::storage::unhashed::get(b":reject-message-fee:") == Some(true) { return Err(TEST_ERROR); } frame_support::storage::unhashed::put(b":message-fee:", &(submitter, fee)); Ok(()) } fn pay_relayers_rewards( _confirmation_relayer: &AccountId, relayers_rewards: RelayersRewards, _relayer_fund_account: &AccountId, ) { for (relayer, reward) in relayers_rewards { let key = (b":relayer-reward:", relayer, reward.reward).encode(); frame_support::storage::unhashed::put(&key, &true); } } } /// Source header chain that is used in tests. #[derive(Debug)] pub struct TestSourceHeaderChain; impl SourceHeaderChain for TestSourceHeaderChain { type Error = &'static str; type MessagesProof = TestMessagesProof; fn verify_messages_proof( proof: Self::MessagesProof, _messages_count: u32, ) -> Result>, Self::Error> { proof .result .map(|proof| proof.into_iter().collect()) .map_err(|_| TEST_ERROR) } } /// Source header chain that is used in tests. #[derive(Debug)] pub struct TestMessageDispatch; impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; fn dispatch_weight(message: &DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.1, Err(_) => 0, } } fn dispatch(_message: DispatchMessage) {} } /// Return test lane message with given nonce and payload. pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce, }, data: message_data(payload), } } /// Return message data with valid fee for given payload. pub fn message_data(payload: TestPayload) -> MessageData { MessageData { payload: payload.encode(), fee: 1, } } /// Run message lane test. pub fn run_test(test: impl FnOnce() -> T) -> T { let mut t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)], } .assimilate_storage(&mut t) .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(test) }