// Copyright 2019-2021 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 . //! Chain-specific relayer configuration. pub mod kusama_headers_to_polkadot; pub mod kusama_messages_to_polkadot; pub mod millau_headers_to_rialto; pub mod millau_messages_to_rialto; pub mod polkadot_headers_to_kusama; pub mod polkadot_messages_to_kusama; pub mod rialto_headers_to_millau; pub mod rialto_messages_to_millau; pub mod rococo_headers_to_wococo; pub mod rococo_messages_to_wococo; pub mod westend_headers_to_millau; pub mod wococo_headers_to_rococo; pub mod wococo_messages_to_rococo; mod kusama; mod millau; mod polkadot; mod rialto; mod rococo; mod westend; mod wococo; // Millau/Rialto tokens have no any real value, so the conversion rate we use is always 1:1. But we want to // test our code that is intended to work with real-value chains. So to keep it close to 1:1, we'll be treating // Rialto as BTC and Millau as wBTC (only in relayer). /// The identifier of token, which value is associated with Rialto token value by relayer. pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = polkadot::TOKEN_ID; /// The identifier of token, which value is associated with Millau token value by relayer. pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = kusama::TOKEN_ID; use relay_utils::metrics::MetricsParams; pub(crate) fn add_polkadot_kusama_price_metrics( prefix: Option, params: MetricsParams, ) -> anyhow::Result { // Polkadot/Kusama prices are added as metrics here, because atm we don't have Polkadot <-> Kusama // relays, but we want to test metrics/dashboards in advance Ok(relay_utils::relay_metrics(prefix, params) .standalone_metric(|registry, prefix| { substrate_relay_helper::helpers::token_price_metric(registry, prefix, "polkadot") })? .standalone_metric(|registry, prefix| { substrate_relay_helper::helpers::token_price_metric(registry, prefix, "kusama") })? .into_params()) } #[cfg(test)] mod tests { use crate::cli::{encode_call, send_message}; use bp_messages::source_chain::TargetHeaderChain; use codec::Encode; use frame_support::dispatch::GetDispatchInfo; use relay_millau_client::Millau; use relay_rialto_client::Rialto; use relay_substrate_client::{TransactionSignScheme, UnsignedTransaction}; use sp_core::Pair; use sp_runtime::traits::{IdentifyAccount, Verify}; #[test] fn millau_signature_is_valid_on_rialto() { let millau_sign = relay_millau_client::SigningParams::from_string("//Dave", None).unwrap(); let call = rialto_runtime::Call::System(rialto_runtime::SystemCall::remark(vec![])); let millau_public: bp_millau::AccountSigner = millau_sign.public().into(); let millau_account_id: bp_millau::AccountId = millau_public.into_account(); let digest = millau_runtime::millau_to_rialto_account_ownership_digest( &call, millau_account_id, rialto_runtime::VERSION.spec_version, ); let rialto_signer = relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap(); let signature = rialto_signer.sign(&digest); assert!(signature.verify(&digest[..], &rialto_signer.public())); } #[test] fn rialto_signature_is_valid_on_millau() { let rialto_sign = relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap(); let call = millau_runtime::Call::System(millau_runtime::SystemCall::remark(vec![])); let rialto_public: bp_rialto::AccountSigner = rialto_sign.public().into(); let rialto_account_id: bp_rialto::AccountId = rialto_public.into_account(); let digest = rialto_runtime::rialto_to_millau_account_ownership_digest( &call, rialto_account_id, millau_runtime::VERSION.spec_version, ); let millau_signer = relay_millau_client::SigningParams::from_string("//Dave", None).unwrap(); let signature = millau_signer.sign(&digest); assert!(signature.verify(&digest[..], &millau_signer.public())); } #[test] fn maximal_rialto_to_millau_message_arguments_size_is_computed_correctly() { use rialto_runtime::millau_messages::Millau; let maximal_remark_size = encode_call::compute_maximal_message_arguments_size( bp_rialto::max_extrinsic_size(), bp_millau::max_extrinsic_size(), ); let call: millau_runtime::Call = millau_runtime::SystemCall::remark(vec![42; maximal_remark_size as _]).into(); let payload = send_message::message_payload( Default::default(), call.get_dispatch_info().weight, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert_eq!(Millau::verify_message(&payload), Ok(())); let call: millau_runtime::Call = millau_runtime::SystemCall::remark(vec![42; (maximal_remark_size + 1) as _]).into(); let payload = send_message::message_payload( Default::default(), call.get_dispatch_info().weight, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert!(Millau::verify_message(&payload).is_err()); } #[test] fn maximal_size_remark_to_rialto_is_generated_correctly() { assert!( bridge_runtime_common::messages::target::maximal_incoming_message_size( bp_rialto::max_extrinsic_size() ) > bp_millau::max_extrinsic_size(), "We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large", ) } #[test] fn maximal_rialto_to_millau_message_dispatch_weight_is_computed_correctly() { use rialto_runtime::millau_messages::Millau; let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight(bp_millau::max_extrinsic_weight()); let call: millau_runtime::Call = rialto_runtime::SystemCall::remark(vec![]).into(); let payload = send_message::message_payload( Default::default(), maximal_dispatch_weight, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert_eq!(Millau::verify_message(&payload), Ok(())); let payload = send_message::message_payload( Default::default(), maximal_dispatch_weight + 1, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert!(Millau::verify_message(&payload).is_err()); } #[test] fn maximal_weight_fill_block_to_rialto_is_generated_correctly() { use millau_runtime::rialto_messages::Rialto; let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight(bp_rialto::max_extrinsic_weight()); let call: rialto_runtime::Call = millau_runtime::SystemCall::remark(vec![]).into(); let payload = send_message::message_payload( Default::default(), maximal_dispatch_weight, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert_eq!(Rialto::verify_message(&payload), Ok(())); let payload = send_message::message_payload( Default::default(), maximal_dispatch_weight + 1, bp_message_dispatch::CallOrigin::SourceRoot, &call, send_message::DispatchFeePayment::AtSourceChain, ); assert!(Rialto::verify_message(&payload).is_err()); } #[test] fn rialto_tx_extra_bytes_constant_is_correct() { let rialto_call = rialto_runtime::Call::System(rialto_runtime::SystemCall::remark(vec![])); let rialto_tx = Rialto::sign_transaction( Default::default(), &sp_keyring::AccountKeyring::Alice.pair(), relay_substrate_client::TransactionEra::immortal(), UnsignedTransaction::new(rialto_call.clone(), 0), ); let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len(); assert!( bp_rialto::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, "Hardcoded number of extra bytes in Rialto transaction {} is lower than actual value: {}", bp_rialto::TX_EXTRA_BYTES, extra_bytes_in_transaction, ); } #[test] fn millau_tx_extra_bytes_constant_is_correct() { let millau_call = millau_runtime::Call::System(millau_runtime::SystemCall::remark(vec![])); let millau_tx = Millau::sign_transaction( Default::default(), &sp_keyring::AccountKeyring::Alice.pair(), relay_substrate_client::TransactionEra::immortal(), UnsignedTransaction::new(millau_call.clone(), 0), ); let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len(); assert!( bp_millau::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, "Hardcoded number of extra bytes in Millau transaction {} is lower than actual value: {}", bp_millau::TX_EXTRA_BYTES, extra_bytes_in_transaction, ); } } #[cfg(test)] mod rococo_tests { use bp_header_chain::justification::GrandpaJustification; use codec::Encode; #[test] fn scale_compatibility_of_bridges_call() { // given let header = sp_runtime::generic::Header { parent_hash: Default::default(), number: Default::default(), state_root: Default::default(), extrinsics_root: Default::default(), digest: sp_runtime::generic::Digest { logs: vec![] }, }; let justification = GrandpaJustification { round: 0, commit: finality_grandpa::Commit { target_hash: Default::default(), target_number: Default::default(), precommits: vec![], }, votes_ancestries: vec![], }; let actual = relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof( header.clone(), justification.clone(), ); let expected = millau_runtime::BridgeGrandpaRialtoCall::::submit_finality_proof( header, justification, ); // when let actual_encoded = actual.encode(); let expected_encoded = expected.encode(); // then assert_eq!( actual_encoded, expected_encoded, "\n\nEncoding difference.\nGot {:#?} \nExpected: {:#?}", actual, expected ); } } #[cfg(test)] mod westend_tests { use bp_header_chain::justification::GrandpaJustification; use codec::Encode; #[test] fn scale_compatibility_of_bridges_call() { // given let header = sp_runtime::generic::Header { parent_hash: Default::default(), number: Default::default(), state_root: Default::default(), extrinsics_root: Default::default(), digest: sp_runtime::generic::Digest { logs: vec![] }, }; let justification = GrandpaJustification { round: 0, commit: finality_grandpa::Commit { target_hash: Default::default(), target_number: Default::default(), precommits: vec![], }, votes_ancestries: vec![], }; let actual = bp_westend::BridgeGrandpaRococoCall::submit_finality_proof(header.clone(), justification.clone()); let expected = millau_runtime::BridgeGrandpaRialtoCall::::submit_finality_proof( header, justification, ); // when let actual_encoded = actual.encode(); let expected_encoded = expected.encode(); // then assert_eq!( actual_encoded, expected_encoded, "\n\nEncoding difference.\nGot {:#?} \nExpected: {:#?}", actual, expected ); } }