From 077e2cc11bb6166ac9f58a0b827d2f11c92a4dbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomusdrw@users.noreply.github.com>
Date: Tue, 2 Mar 2021 01:26:03 +0100
Subject: [PATCH] Derive bridged accounts (#781)

* Add derivation support.

* Add custom decoder for Accounts.

* cargo fmt --all

* Fix warn.

* Fix articles.

Co-authored-by: Hernando Castano <castano.ha@gmail.com>
---
 bridges/relays/substrate/src/cli.rs  | 70 ++++++++++++++++++++++++++--
 bridges/relays/substrate/src/main.rs | 32 ++++++++++++-
 2 files changed, 96 insertions(+), 6 deletions(-)

diff --git a/bridges/relays/substrate/src/cli.rs b/bridges/relays/substrate/src/cli.rs
index 090ff3f92a9..9914cd2e3a5 100644
--- a/bridges/relays/substrate/src/cli.rs
+++ b/bridges/relays/substrate/src/cli.rs
@@ -20,6 +20,7 @@ use bp_message_lane::LaneId;
 use frame_support::weights::Weight;
 use sp_core::Bytes;
 use sp_finality_grandpa::SetId as GrandpaAuthoritiesSetId;
+use sp_runtime::app_crypto::Ss58Codec;
 use structopt::{clap::arg_enum, StructOpt};
 
 /// Parse relay CLI args.
@@ -63,6 +64,8 @@ pub enum Command {
 	EncodeMessagePayload(EncodeMessagePayload),
 	/// Estimate Delivery and Dispatch Fee required for message submission to message lane.
 	EstimateFee(EstimateFee),
+	/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
+	DeriveAccount(DeriveAccount),
 }
 
 /// Start headers relayer process.
@@ -268,6 +271,20 @@ pub enum EstimateFee {
 	},
 }
 
+/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
+///
+/// The (derived) target chain `AccountId` is going to be used as dispatch origin of the call
+/// that has been sent over the bridge.
+/// This account can also be used to receive target-chain funds (or other form of ownership),
+/// since messages sent over the bridge will be able to spend these.
+#[derive(StructOpt)]
+pub enum DeriveAccount {
+	/// Given Rialto AccountId, display corresponding Millau AccountId.
+	RialtoToMillau { account: AccountId },
+	/// Given Millau AccountId, display corresponding Rialto AccountId.
+	MillauToRialto { account: AccountId },
+}
+
 /// MessagePayload that can be delivered to message lane pallet on Millau.
 #[derive(StructOpt, Debug)]
 pub enum MillauToRialtoMessagePayload {
@@ -284,7 +301,7 @@ pub enum MillauToRialtoMessagePayload {
 		message: ToRialtoMessage,
 		/// SS58 encoded account that will send the payload (must have SS58Prefix = 42)
 		#[structopt(long)]
-		sender: bp_rialto::AccountId,
+		sender: AccountId,
 	},
 }
 
@@ -305,7 +322,7 @@ pub enum RialtoToMillauMessagePayload {
 
 		/// SS58 encoded account that will send the payload (must have SS58Prefix = 42)
 		#[structopt(long)]
-		sender: bp_rialto::AccountId,
+		sender: AccountId,
 	},
 }
 
@@ -327,7 +344,7 @@ pub enum ToRialtoMessage {
 	Transfer {
 		/// SS58 encoded account that will receive the transfer (must have SS58Prefix = 42)
 		#[structopt(long)]
-		recipient: bp_rialto::AccountId,
+		recipient: AccountId,
 		/// Amount of target tokens to send.
 		#[structopt(long)]
 		amount: bp_rialto::Balance,
@@ -352,7 +369,7 @@ pub enum ToMillauMessage {
 	Transfer {
 		/// SS58 encoded account that will receive the transfer (must have SS58Prefix = 42)
 		#[structopt(long)]
-		recipient: bp_millau::AccountId,
+		recipient: AccountId,
 		/// Amount of target tokens to send.
 		#[structopt(long)]
 		amount: bp_millau::Balance,
@@ -371,6 +388,51 @@ arg_enum! {
 	}
 }
 
+/// Generic account id with custom parser.
+#[derive(Debug)]
+pub struct AccountId {
+	account: sp_runtime::AccountId32,
+	version: sp_core::crypto::Ss58AddressFormat,
+}
+
+impl std::str::FromStr for AccountId {
+	type Err = String;
+
+	fn from_str(s: &str) -> Result<Self, Self::Err> {
+		let (account, version) = sp_runtime::AccountId32::from_ss58check_with_version(s)
+			.map_err(|err| format!("Unable to decode SS58 address: {:?}", err))?;
+		Ok(Self { account, version })
+	}
+}
+
+impl AccountId {
+	/// Perform runtime checks of SS58 version and get Rialto's AccountId.
+	pub fn into_rialto(self) -> bp_rialto::AccountId {
+		self.check_and_get("Rialto", rialto_runtime::SS58Prefix::get())
+	}
+
+	/// Perform runtime checks of SS58 version and get Millau's AccountId.
+	pub fn into_millau(self) -> bp_millau::AccountId {
+		self.check_and_get("Millau", millau_runtime::SS58Prefix::get())
+	}
+
+	/// Check SS58Prefix and return the account id.
+	fn check_and_get(self, net: &str, expected_prefix: u8) -> sp_runtime::AccountId32 {
+		let version: u16 = self.version.into();
+		println!("Version: {} vs {}", version, expected_prefix);
+		if version != expected_prefix as u16 {
+			log::warn!(
+				target: "bridge",
+				"Following address: {} does not seem to match {}'s format, got: {}",
+				self.account,
+				net,
+				self.version,
+			)
+		}
+		self.account
+	}
+}
+
 /// Lane id.
 #[derive(Debug)]
 pub struct HexLaneId(LaneId);
diff --git a/bridges/relays/substrate/src/main.rs b/bridges/relays/substrate/src/main.rs
index ed17e309670..f5c8c6a1e43 100644
--- a/bridges/relays/substrate/src/main.rs
+++ b/bridges/relays/substrate/src/main.rs
@@ -68,6 +68,7 @@ async fn run_command(command: cli::Command) -> Result<(), String> {
 		cli::Command::EncodeCall(arg) => run_encode_call(arg).await,
 		cli::Command::EncodeMessagePayload(arg) => run_encode_message_payload(arg).await,
 		cli::Command::EstimateFee(arg) => run_estimate_fee(arg).await,
+		cli::Command::DeriveAccount(arg) => run_derive_account(arg).await,
 	}
 }
 
@@ -412,6 +413,31 @@ async fn run_estimate_fee(cmd: cli::EstimateFee) -> Result<(), String> {
 	Ok(())
 }
 
+async fn run_derive_account(cmd: cli::DeriveAccount) -> Result<(), String> {
+	match cmd {
+		cli::DeriveAccount::RialtoToMillau { account } => {
+			let account = account.into_rialto();
+			let acc = bp_runtime::SourceAccount::Account(account.clone());
+			let id = bp_millau::derive_account_from_rialto_id(acc);
+			println!(
+				"{} (Rialto)\n\nCorresponding (derived) account id:\n-> {} (Millau)",
+				account, id
+			)
+		}
+		cli::DeriveAccount::MillauToRialto { account } => {
+			let account = account.into_millau();
+			let acc = bp_runtime::SourceAccount::Account(account.clone());
+			let id = bp_rialto::derive_account_from_millau_id(acc);
+			println!(
+				"{} (Millau)\n\nCorresponding (derived) account id:\n-> {} (Rialto)",
+				account, id
+			)
+		}
+	}
+
+	Ok(())
+}
+
 async fn estimate_message_delivery_and_dispatch_fee<Fee: Decode, C: Chain, P: Encode>(
 	client: &relay_substrate_client::Client<C>,
 	estimate_fee_method: &str,
@@ -615,7 +641,7 @@ impl crate::cli::MillauToRialtoMessagePayload {
 				.map_err(|e| format!("Failed to decode Millau's MessagePayload: {:?}", e)),
 			Self::Message { message, sender } => {
 				let spec_version = rialto_runtime::VERSION.spec_version;
-				let origin = CallOrigin::SourceAccount(sender);
+				let origin = CallOrigin::SourceAccount(sender.into_millau());
 				let call = message.into_call()?;
 				let weight = call.get_dispatch_info().weight;
 
@@ -635,7 +661,7 @@ impl crate::cli::RialtoToMillauMessagePayload {
 				.map_err(|e| format!("Failed to decode Rialto's MessagePayload: {:?}", e)),
 			Self::Message { message, sender } => {
 				let spec_version = millau_runtime::VERSION.spec_version;
-				let origin = CallOrigin::SourceAccount(sender);
+				let origin = CallOrigin::SourceAccount(sender.into_rialto());
 				let call = message.into_call()?;
 				let weight = call.get_dispatch_info().weight;
 
@@ -699,6 +725,7 @@ impl crate::cli::ToRialtoMessage {
 				)))
 			}
 			cli::ToRialtoMessage::Transfer { recipient, amount } => {
+				let recipient = recipient.into_rialto();
 				rialto_runtime::Call::Balances(rialto_runtime::BalancesCall::transfer(recipient, amount))
 			}
 		};
@@ -728,6 +755,7 @@ impl crate::cli::ToMillauMessage {
 				)))
 			}
 			cli::ToMillauMessage::Transfer { recipient, amount } => {
+				let recipient = recipient.into_millau();
 				millau_runtime::Call::Balances(millau_runtime::BalancesCall::transfer(recipient, amount))
 			}
 		};
-- 
GitLab