Reorganize relay code to make it easy to add new networks. (#813)

* Nest some crates.

* Alter command execution to make it easier to add new bridges.

* Rename sub-dirs.

* cargo fmt --all

* Address clippy.

* Update relays/substrate/src/rialto_millau/cli.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
This commit is contained in:
Tomasz Drwięga
2021-03-12 06:50:20 +01:00
committed by Bastian Köcher
parent 53cdf66071
commit d9bec5f387
66 changed files with 1546 additions and 1281 deletions
@@ -0,0 +1,19 @@
[package]
name = "relay-ethereum-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
bp-eth-poa = { path = "../../../primitives/ethereum-poa" }
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
hex-literal = "0.3"
jsonrpsee-proc-macros = "0.2.0-alpha"
jsonrpsee-types = "0.2.0-alpha"
jsonrpsee-ws-client = "0.2.0-alpha"
libsecp256k1 = { version = "0.3.4", default-features = false, features = ["hmac"] }
log = "0.4.11"
relay-utils = { path = "../../generic/utils" }
web3 = { version = "0.15", git = "https://github.com/tomusdrw/rust-web3", branch ="td-ethabi", default-features = false }
@@ -0,0 +1,172 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::rpc::Ethereum;
use crate::types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SignedRawTx, SyncState, Transaction,
TransactionHash, H256, U256,
};
use crate::{ConnectionParams, Error, Result};
use jsonrpsee_ws_client::{WsClient as RpcClient, WsConfig as RpcConfig};
use std::sync::Arc;
/// Number of headers missing from the Ethereum node for us to consider node not synced.
const MAJOR_SYNC_BLOCKS: u64 = 5;
/// The client used to interact with an Ethereum node through RPC.
#[derive(Clone)]
pub struct Client {
params: ConnectionParams,
client: Arc<RpcClient>,
}
impl Client {
/// Create a new Ethereum RPC Client.
pub async fn new(params: ConnectionParams) -> Result<Self> {
Ok(Self {
client: Self::build_client(&params).await?,
params,
})
}
/// Build client to use in connection.
async fn build_client(params: &ConnectionParams) -> Result<Arc<RpcClient>> {
let uri = format!("ws://{}:{}", params.host, params.port);
let client = RpcClient::new(RpcConfig::with_url(&uri)).await?;
Ok(Arc::new(client))
}
/// Reopen client connection.
pub async fn reconnect(&mut self) -> Result<()> {
self.client = Self::build_client(&self.params).await?;
Ok(())
}
}
impl Client {
/// Returns true if client is connected to at least one peer and is in synced state.
pub async fn ensure_synced(&self) -> Result<()> {
match Ethereum::syncing(&*self.client).await? {
SyncState::NotSyncing => Ok(()),
SyncState::Syncing(syncing) => {
let missing_headers = syncing.highest_block.saturating_sub(syncing.current_block);
if missing_headers > MAJOR_SYNC_BLOCKS.into() {
return Err(Error::ClientNotSynced(missing_headers));
}
Ok(())
}
}
}
/// Estimate gas usage for the given call.
pub async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256> {
Ok(Ethereum::estimate_gas(&*self.client, call_request).await?)
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn best_block_number(&self) -> Result<u64> {
Ok(Ethereum::block_number(&*self.client).await?.as_u64())
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn header_by_number(&self, block_number: u64) -> Result<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_number(&*self.client, block_number, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}
}
/// Retrieve block header by its hash from Ethereum node.
pub async fn header_by_hash(&self, hash: H256) -> Result<Header> {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_hash(&*self.client, hash, get_full_tx_objects).await?;
match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() {
true => Ok(header),
false => Err(Error::IncompleteHeader),
}
}
/// Retrieve block header and its transactions by its number from Ethereum node.
pub async fn header_by_number_with_transactions(&self, number: u64) -> Result<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header =
Ethereum::get_block_by_number_with_transactions(&*self.client, number, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(Error::IncompleteHeader);
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(Error::IncompleteTransaction);
}
Ok(header)
}
/// Retrieve block header and its transactions by its hash from Ethereum node.
pub async fn header_by_hash_with_transactions(&self, hash: H256) -> Result<HeaderWithTransactions> {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_hash_with_transactions(&*self.client, hash, get_full_tx_objects).await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
if !is_complete_header {
return Err(Error::IncompleteHeader);
}
let is_complete_transactions = header.transactions.iter().all(|tx| tx.raw.is_some());
if !is_complete_transactions {
return Err(Error::IncompleteTransaction);
}
Ok(header)
}
/// Retrieve transaction by its hash from Ethereum node.
pub async fn transaction_by_hash(&self, hash: H256) -> Result<Option<Transaction>> {
Ok(Ethereum::transaction_by_hash(&*self.client, hash).await?)
}
/// Retrieve transaction receipt by transaction hash.
pub async fn transaction_receipt(&self, transaction_hash: H256) -> Result<Receipt> {
Ok(Ethereum::get_transaction_receipt(&*self.client, transaction_hash).await?)
}
/// Get the nonce of the given account.
pub async fn account_nonce(&self, address: Address) -> Result<U256> {
Ok(Ethereum::get_transaction_count(&*self.client, address).await?)
}
/// Submit an Ethereum transaction.
///
/// The transaction must already be signed before sending it through this method.
pub async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> Result<TransactionHash> {
let transaction = Bytes(signed_raw_tx);
let tx_hash = Ethereum::submit_transaction(&*self.client, transaction).await?;
log::trace!(target: "bridge", "Sent transaction to Ethereum node: {:?}", tx_hash);
Ok(tx_hash)
}
/// Call Ethereum smart contract.
pub async fn eth_call(&self, call_transaction: CallRequest) -> Result<Bytes> {
Ok(Ethereum::call(&*self.client, call_transaction).await?)
}
}
@@ -0,0 +1,86 @@
// 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 <http://www.gnu.org/licenses/>.
//! Ethereum node RPC errors.
use crate::types::U256;
use jsonrpsee_types::error::Error as RpcError;
use relay_utils::MaybeConnectionError;
/// Result type used by Ethereum client.
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can occur only when interacting with
/// an Ethereum node through RPC.
#[derive(Debug)]
pub enum Error {
/// An error that can occur when making an HTTP request to
/// an JSON-RPC client.
RpcError(RpcError),
/// Failed to parse response.
ResponseParseFailed(String),
/// We have received a header with missing fields.
IncompleteHeader,
/// We have received a transaction missing a `raw` field.
IncompleteTransaction,
/// An invalid Substrate block number was received from
/// an Ethereum node.
InvalidSubstrateBlockNumber,
/// An invalid index has been received from an Ethereum node.
InvalidIncompleteIndex,
/// The client we're connected to is not synced, so we can't rely on its state. Contains
/// number of unsynced headers.
ClientNotSynced(U256),
}
impl From<RpcError> for Error {
fn from(error: RpcError) -> Self {
Error::RpcError(error)
}
}
impl MaybeConnectionError for Error {
fn is_connection_error(&self) -> bool {
matches!(
*self,
Error::RpcError(RpcError::TransportError(_))
// right now if connection to the ws server is dropped (after it is already established),
// we're getting this error
| Error::RpcError(RpcError::Internal(_))
| Error::ClientNotSynced(_),
)
}
}
impl ToString for Error {
fn to_string(&self) -> String {
match self {
Self::RpcError(e) => e.to_string(),
Self::ResponseParseFailed(e) => e.to_string(),
Self::IncompleteHeader => {
"Incomplete Ethereum Header Received (missing some of required fields - hash, number, logs_bloom)"
.to_string()
}
Self::IncompleteTransaction => "Incomplete Ethereum Transaction (missing required field - raw)".to_string(),
Self::InvalidSubstrateBlockNumber => "Received an invalid Substrate block from Ethereum Node".to_string(),
Self::InvalidIncompleteIndex => "Received an invalid incomplete index from Ethereum Node".to_string(),
Self::ClientNotSynced(missing_headers) => {
format!("Ethereum client is not synced: syncing {} headers", missing_headers)
}
}
}
}
@@ -0,0 +1,48 @@
// 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 <http://www.gnu.org/licenses/>.
//! Tools to interact with (Open) Ethereum node using RPC methods.
#![warn(missing_docs)]
mod client;
mod error;
mod rpc;
mod sign;
pub use crate::client::Client;
pub use crate::error::{Error, Result};
pub use crate::sign::{sign_and_submit_transaction, SigningParams};
pub mod types;
/// Ethereum-over-websocket connection params.
#[derive(Debug, Clone)]
pub struct ConnectionParams {
/// Websocket server hostname.
pub host: String,
/// Websocket server TCP port.
pub port: u16,
}
impl Default for ConnectionParams {
fn default() -> Self {
ConnectionParams {
host: "localhost".into(),
port: 8546,
}
}
}
@@ -0,0 +1,56 @@
// 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 <http://www.gnu.org/licenses/>.
//! Ethereum node RPC interface.
// The compiler doesn't think we're using the
// code from rpc_api!
#![allow(dead_code)]
#![allow(unused_variables)]
use crate::types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SyncState, Transaction, TransactionHash,
H256, U256, U64,
};
jsonrpsee_proc_macros::rpc_client_api! {
pub(crate) Ethereum {
#[rpc(method = "eth_syncing", positional_params)]
fn syncing() -> SyncState;
#[rpc(method = "eth_estimateGas", positional_params)]
fn estimate_gas(call_request: CallRequest) -> U256;
#[rpc(method = "eth_blockNumber", positional_params)]
fn block_number() -> U64;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number(block_number: U64, full_tx_objs: bool) -> Header;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash(hash: H256, full_tx_objs: bool) -> Header;
#[rpc(method = "eth_getBlockByNumber", positional_params)]
fn get_block_by_number_with_transactions(number: U64, full_tx_objs: bool) -> HeaderWithTransactions;
#[rpc(method = "eth_getBlockByHash", positional_params)]
fn get_block_by_hash_with_transactions(hash: H256, full_tx_objs: bool) -> HeaderWithTransactions;
#[rpc(method = "eth_getTransactionByHash", positional_params)]
fn transaction_by_hash(hash: H256) -> Option<Transaction>;
#[rpc(method = "eth_getTransactionReceipt", positional_params)]
fn get_transaction_receipt(transaction_hash: H256) -> Receipt;
#[rpc(method = "eth_getTransactionCount", positional_params)]
fn get_transaction_count(address: Address) -> U256;
#[rpc(method = "eth_submitTransaction", positional_params)]
fn submit_transaction(transaction: Bytes) -> TransactionHash;
#[rpc(method = "eth_call", positional_params)]
fn call(transaction_call: CallRequest) -> Bytes;
}
}
@@ -0,0 +1,85 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::types::{Address, CallRequest, U256};
use crate::{Client, Result};
use bp_eth_poa::signatures::{secret_to_address, SignTransaction};
use hex_literal::hex;
use secp256k1::SecretKey;
/// Ethereum signing params.
#[derive(Clone, Debug)]
pub struct SigningParams {
/// Ethereum chain id.
pub chain_id: u64,
/// Ethereum transactions signer.
pub signer: SecretKey,
/// Gas price we agree to pay.
pub gas_price: U256,
}
impl Default for SigningParams {
fn default() -> Self {
SigningParams {
chain_id: 0x11, // Parity dev chain
// account that has a lot of ether when we run instant seal engine
// address: 0x00a329c0648769a73afac7f9381e08fb43dbea72
// secret: 0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7
signer: SecretKey::parse(&hex!(
"4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7"
))
.expect("secret is hardcoded, thus valid; qed"),
gas_price: 8_000_000_000u64.into(), // 8 Gwei
}
}
}
/// Sign and submit tranaction using given Ethereum client.
pub async fn sign_and_submit_transaction(
client: &Client,
params: &SigningParams,
contract_address: Option<Address>,
nonce: Option<U256>,
double_gas: bool,
encoded_call: Vec<u8>,
) -> Result<()> {
let nonce = if let Some(n) = nonce {
n
} else {
let address: Address = secret_to_address(&params.signer);
client.account_nonce(address).await?
};
let call_request = CallRequest {
to: contract_address,
data: Some(encoded_call.clone().into()),
..Default::default()
};
let gas = client.estimate_gas(call_request).await?;
let raw_transaction = bp_eth_poa::UnsignedTransaction {
nonce,
to: contract_address,
value: U256::zero(),
gas: if double_gas { gas.saturating_mul(2.into()) } else { gas },
gas_price: params.gas_price,
payload: encoded_call,
}
.sign_by(&params.signer, Some(params.chain_id));
let _ = client.submit_transaction(raw_transaction).await?;
Ok(())
}
@@ -0,0 +1,80 @@
// 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 <http://www.gnu.org/licenses/>.
//! Common types that are used in relay <-> Ethereum node communications.
use headers_relay::sync_types::SourceHeader;
pub use web3::types::{Address, Bytes, CallRequest, SyncState, H256, U128, U256, U64};
/// When header is just received from the Ethereum node, we check that it has
/// both number and hash fields filled.
pub const HEADER_ID_PROOF: &str = "checked on retrieval; qed";
/// Ethereum transaction hash type.
pub type HeaderHash = H256;
/// Ethereum transaction hash type.
pub type TransactionHash = H256;
/// Ethereum transaction type.
pub type Transaction = web3::types::Transaction;
/// Ethereum header type.
pub type Header = web3::types::Block<H256>;
/// Ethereum header type used in headers sync.
#[derive(Clone, Debug, PartialEq)]
pub struct SyncHeader(Header);
impl std::ops::Deref for SyncHeader {
type Target = Header;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// Ethereum header with transactions type.
pub type HeaderWithTransactions = web3::types::Block<Transaction>;
/// Ethereum transaction receipt type.
pub type Receipt = web3::types::TransactionReceipt;
/// Ethereum header ID.
pub type HeaderId = relay_utils::HeaderId<H256, u64>;
/// A raw Ethereum transaction that's been signed.
pub type SignedRawTx = Vec<u8>;
impl From<Header> for SyncHeader {
fn from(header: Header) -> Self {
Self(header)
}
}
impl SourceHeader<H256, u64> for SyncHeader {
fn id(&self) -> HeaderId {
relay_utils::HeaderId(
self.number.expect(HEADER_ID_PROOF).as_u64(),
self.hash.expect(HEADER_ID_PROOF),
)
}
fn parent_id(&self) -> HeaderId {
relay_utils::HeaderId(self.number.expect(HEADER_ID_PROOF).as_u64() - 1, self.parent_hash)
}
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "relay-kusama-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
relay-substrate-client = { path = "../substrate" }
relay-utils = { path = "../../generic/utils" }
# Bridge dependencies
bp-kusama = { path = "../../../primitives/chains/kusama" }
# Substrate Dependencies
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
+47
View File
@@ -0,0 +1,47 @@
// 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 <http://www.gnu.org/licenses/>.
//! Types used to connect to the Kusama chain.
use relay_substrate_client::{Chain, ChainBase};
use std::time::Duration;
/// Kusama header id.
pub type HeaderId = relay_utils::HeaderId<bp_kusama::Hash, bp_kusama::BlockNumber>;
/// Kusama chain definition
#[derive(Debug, Clone, Copy)]
pub struct Kusama;
impl ChainBase for Kusama {
type BlockNumber = bp_kusama::BlockNumber;
type Hash = bp_kusama::Hash;
type Hasher = bp_kusama::Hasher;
type Header = bp_kusama::Header;
}
impl Chain for Kusama {
const NAME: &'static str = "Kusama";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
type AccountId = bp_kusama::AccountId;
type Index = bp_kusama::Nonce;
type SignedBlock = bp_kusama::SignedBlock;
type Call = ();
}
/// Kusama header type used in headers sync.
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_kusama::Header>;
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "relay-millau-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
relay-substrate-client = { path = "../../clients/substrate" }
relay-utils = { path = "../../generic/utils" }
# Supported Chains
millau-runtime = { path = "../../../bin/millau/runtime" }
# Substrate Dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
+125
View File
@@ -0,0 +1,125 @@
// 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 <http://www.gnu.org/licenses/>.
//! Types used to connect to the Millau-Substrate chain.
use codec::Encode;
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration;
pub use millau_runtime::BridgeRialtoCall;
/// Millau header id.
pub type HeaderId = relay_utils::HeaderId<millau_runtime::Hash, millau_runtime::BlockNumber>;
/// Millau chain definition.
#[derive(Debug, Clone, Copy)]
pub struct Millau;
impl ChainBase for Millau {
type BlockNumber = millau_runtime::BlockNumber;
type Hash = millau_runtime::Hash;
type Hasher = millau_runtime::Hashing;
type Header = millau_runtime::Header;
}
impl Chain for Millau {
const NAME: &'static str = "Millau";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
type AccountId = millau_runtime::AccountId;
type Index = millau_runtime::Index;
type SignedBlock = millau_runtime::SignedBlock;
type Call = millau_runtime::Call;
}
impl ChainWithBalances for Millau {
type NativeBalance = millau_runtime::Balance;
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
use frame_support::storage::generator::StorageMap;
StorageKey(frame_system::Account::<millau_runtime::Runtime>::storage_map_final_key(
account_id,
))
}
}
impl TransactionSignScheme for Millau {
type Chain = Millau;
type AccountKeyPair = sp_core::sr25519::Pair;
type SignedTransaction = millau_runtime::UncheckedExtrinsic;
fn sign_transaction(
genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call,
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::from_raw(
call,
(
frame_system::CheckSpecVersion::<millau_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<millau_runtime::Runtime>::new(),
frame_system::CheckGenesis::<millau_runtime::Runtime>::new(),
frame_system::CheckEra::<millau_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
frame_system::CheckNonce::<millau_runtime::Runtime>::from(signer_nonce),
frame_system::CheckWeight::<millau_runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<millau_runtime::Runtime>::from(0),
),
(
millau_runtime::VERSION.spec_version,
millau_runtime::VERSION.transaction_version,
genesis_hash,
genesis_hash,
(),
(),
(),
),
);
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
let signer: sp_runtime::MultiSigner = signer.public().into();
let (call, extra, _) = raw_payload.deconstruct();
millau_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
}
}
/// Millau signing params.
#[derive(Clone)]
pub struct SigningParams {
/// Substrate transactions signer.
pub signer: sp_core::sr25519::Pair,
}
impl SigningParams {
/// Create signing params from SURI and password.
pub fn from_suri(suri: &str, password: Option<&str>) -> Result<Self, sp_core::crypto::SecretStringError> {
Ok(SigningParams {
signer: sp_core::sr25519::Pair::from_string(suri, password)?,
})
}
}
impl std::fmt::Debug for SigningParams {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.signer.public())
}
}
/// Millau header type used in headers sync.
pub type SyncHeader = relay_substrate_client::SyncHeader<millau_runtime::Header>;
@@ -0,0 +1,25 @@
[package]
name = "relay-polkadot-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
relay-substrate-client = { path = "../substrate" }
relay-utils = { path = "../../generic/utils" }
# Bridge dependencies
bp-polkadot = { path = "../../../primitives/chains/polkadot" }
# Substrate Dependencies
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -0,0 +1,47 @@
// 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 <http://www.gnu.org/licenses/>.
//! Types used to connect to the Polkadot chain.
use relay_substrate_client::{Chain, ChainBase};
use std::time::Duration;
/// Polkadot header id.
pub type HeaderId = relay_utils::HeaderId<bp_polkadot::Hash, bp_polkadot::BlockNumber>;
/// Polkadot chain definition
#[derive(Debug, Clone, Copy)]
pub struct Polkadot;
impl ChainBase for Polkadot {
type BlockNumber = bp_polkadot::BlockNumber;
type Hash = bp_polkadot::Hash;
type Hasher = bp_polkadot::Hasher;
type Header = bp_polkadot::Header;
}
impl Chain for Polkadot {
const NAME: &'static str = "Polkadot";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
type AccountId = bp_polkadot::AccountId;
type Index = bp_polkadot::Nonce;
type SignedBlock = bp_polkadot::SignedBlock;
type Call = ();
}
/// Polkadot header type used in headers sync.
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_polkadot::Header>;
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "relay-rialto-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
headers-relay = { path = "../../generic/headers" }
relay-substrate-client = { path = "../substrate" }
relay-utils = { path = "../../generic/utils" }
# Bridge dependencies
rialto-runtime = { path = "../../../bin/rialto/runtime" }
# Substrate Dependencies
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
+133
View File
@@ -0,0 +1,133 @@
// 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 <http://www.gnu.org/licenses/>.
//! Types used to connect to the Rialto-Substrate chain.
use codec::Encode;
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration;
pub use rialto_runtime::BridgeMillauCall;
/// Rialto header id.
pub type HeaderId = relay_utils::HeaderId<rialto_runtime::Hash, rialto_runtime::BlockNumber>;
/// Rialto chain definition
#[derive(Debug, Clone, Copy)]
pub struct Rialto;
impl ChainBase for Rialto {
type BlockNumber = rialto_runtime::BlockNumber;
type Hash = rialto_runtime::Hash;
type Hasher = rialto_runtime::Hashing;
type Header = rialto_runtime::Header;
}
impl Chain for Rialto {
const NAME: &'static str = "Rialto";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
type AccountId = rialto_runtime::AccountId;
type Index = rialto_runtime::Index;
type SignedBlock = rialto_runtime::SignedBlock;
type Call = rialto_runtime::Call;
}
impl ChainWithBalances for Rialto {
type NativeBalance = rialto_runtime::Balance;
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
use frame_support::storage::generator::StorageMap;
StorageKey(frame_system::Account::<rialto_runtime::Runtime>::storage_map_final_key(
account_id,
))
}
}
impl TransactionSignScheme for Rialto {
type Chain = Rialto;
type AccountKeyPair = sp_core::sr25519::Pair;
type SignedTransaction = rialto_runtime::UncheckedExtrinsic;
fn sign_transaction(
genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call,
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::from_raw(
call,
(
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
frame_system::CheckEra::<rialto_runtime::Runtime>::from(sp_runtime::generic::Era::Immortal),
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(signer_nonce),
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(0),
),
(
rialto_runtime::VERSION.spec_version,
rialto_runtime::VERSION.transaction_version,
genesis_hash,
genesis_hash,
(),
(),
(),
),
);
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
let signer: sp_runtime::MultiSigner = signer.public().into();
let (call, extra, _) = raw_payload.deconstruct();
rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
}
}
/// Rialto signing params.
#[derive(Clone)]
pub struct SigningParams {
/// Substrate transactions signer.
pub signer: sp_core::sr25519::Pair,
}
impl SigningParams {
/// Create signing params from SURI and password.
pub fn from_suri(suri: &str, password: Option<&str>) -> Result<Self, sp_core::crypto::SecretStringError> {
Ok(SigningParams {
signer: sp_core::sr25519::Pair::from_string(suri, password)?,
})
}
}
impl std::fmt::Debug for SigningParams {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.signer.public())
}
}
impl Default for SigningParams {
fn default() -> Self {
SigningParams {
signer: sp_keyring::AccountKeyring::Alice.pair(),
}
}
}
/// Rialto header type used in headers sync.
pub type SyncHeader = relay_substrate_client::SyncHeader<rialto_runtime::Header>;
@@ -0,0 +1,41 @@
[package]
name = "relay-substrate-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
async-std = "1.6.5"
async-trait = "0.1.40"
codec = { package = "parity-scale-codec", version = "2.0.0" }
jsonrpsee-proc-macros = "0.2.0-alpha"
jsonrpsee-types = "0.2.0-alpha"
jsonrpsee-ws-client = "0.2.0-alpha"
log = "0.4.11"
num-traits = "0.2"
rand = "0.7"
# Bridge dependencies
bp-header-chain = { path = "../../../primitives/header-chain" }
bp-message-lane = { path = "../../../primitives/message-lane" }
bp-runtime = { path = "../../../primitives/runtime" }
finality-relay = { path = "../../generic/finality" }
headers-relay = { path = "../../generic/headers" }
relay-utils = { path = "../../generic/utils" }
# Substrate Dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" }
#[dev-dependencies]
futures = "0.3.7"
@@ -0,0 +1,103 @@
// 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 <http://www.gnu.org/licenses/>.
use bp_runtime::Chain as ChainBase;
use frame_support::Parameter;
use jsonrpsee_types::jsonrpc::{DeserializeOwned, Serialize};
use num_traits::{CheckedSub, Zero};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{
generic::SignedBlock,
traits::{
AtLeast32Bit, Block as BlockT, Dispatchable, MaybeDisplay, MaybeSerialize, MaybeSerializeDeserialize, Member,
},
Justification,
};
use std::{fmt::Debug, time::Duration};
/// Substrate-based chain from minimal relay-client point of view.
pub trait Chain: ChainBase {
/// Chain name.
const NAME: &'static str;
/// Average block interval.
///
/// How often blocks are produced on that chain. It's suggested to set this value
/// to match the block time of the chain.
const AVERAGE_BLOCK_INTERVAL: Duration;
/// The user account identifier type for the runtime.
type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + Default;
/// Index of a transaction used by the chain.
type Index: Parameter
+ Member
+ MaybeSerialize
+ Debug
+ Default
+ MaybeDisplay
+ DeserializeOwned
+ AtLeast32Bit
+ Copy;
/// Block type.
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
/// The aggregated `Call` type.
type Call: Dispatchable + Debug;
}
/// Substrate-based chain with `frame_system::Config::AccountData` set to
/// the `pallet_balances::AccountData<NativeBalance>`.
pub trait ChainWithBalances: Chain {
/// Balance of an account in native tokens.
type NativeBalance: Parameter + Member + DeserializeOwned + Clone + Copy + CheckedSub + PartialOrd + Zero;
/// Return runtime storage key for getting `frame_system::AccountInfo` of given account.
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey;
}
/// Block with justification.
pub trait BlockWithJustification<Header> {
/// Return block header.
fn header(&self) -> Header;
/// Return block justification, if known.
fn justification(&self) -> Option<&Justification>;
}
/// Substrate-based chain transactions signing scheme.
pub trait TransactionSignScheme {
/// Chain that this scheme is to be used.
type Chain: Chain;
/// Type of key pairs used to sign transactions.
type AccountKeyPair: Pair;
/// Signed transaction.
type SignedTransaction;
/// Create transaction for given runtime call, signed by given account.
fn sign_transaction(
genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call,
) -> Self::SignedTransaction;
}
impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block> {
fn header(&self) -> Block::Header {
self.block.header().clone()
}
fn justification(&self) -> Option<&Justification> {
self.justification.as_ref()
}
}
@@ -0,0 +1,269 @@
// 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 <http://www.gnu.org/licenses/>.
//! Substrate node client.
use crate::chain::{Chain, ChainWithBalances};
use crate::rpc::{Substrate, SubstrateMessageLane};
use crate::{ConnectionParams, Error, Result};
use bp_message_lane::{LaneId, MessageNonce};
use bp_runtime::InstanceId;
use codec::Decode;
use frame_system::AccountInfo;
use jsonrpsee_types::{jsonrpc::DeserializeOwned, traits::SubscriptionClient};
use jsonrpsee_ws_client::{WsClient as RpcClient, WsConfig as RpcConfig, WsSubscription as Subscription};
use num_traits::Zero;
use pallet_balances::AccountData;
use sp_core::Bytes;
use sp_trie::StorageProof;
use sp_version::RuntimeVersion;
use std::ops::RangeInclusive;
const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
const MAX_SUBSCRIPTION_CAPACITY: usize = 4096;
/// Opaque justifications subscription type.
pub type JustificationsSubscription = Subscription<Bytes>;
/// Opaque GRANDPA authorities set.
pub type OpaqueGrandpaAuthoritiesSet = Vec<u8>;
/// Substrate client type.
///
/// Cloning `Client` is a cheap operation.
pub struct Client<C: Chain> {
/// Client connection params.
params: ConnectionParams,
/// Substrate RPC client.
client: RpcClient,
/// Genesis block hash.
genesis_hash: C::Hash,
}
impl<C: Chain> Clone for Client<C> {
fn clone(&self) -> Self {
Client {
params: self.params.clone(),
client: self.client.clone(),
genesis_hash: self.genesis_hash,
}
}
}
impl<C: Chain> std::fmt::Debug for Client<C> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("Client")
.field("genesis_hash", &self.genesis_hash)
.finish()
}
}
impl<C: Chain> Client<C> {
/// Returns client that is able to call RPCs on Substrate node over websocket connection.
pub async fn new(params: ConnectionParams) -> Result<Self> {
let client = Self::build_client(params.clone()).await?;
let number: C::BlockNumber = Zero::zero();
let genesis_hash = Substrate::<C>::chain_get_block_hash(&client, number).await?;
Ok(Self {
params,
client,
genesis_hash,
})
}
/// Reopen client connection.
pub async fn reconnect(&mut self) -> Result<()> {
self.client = Self::build_client(self.params.clone()).await?;
Ok(())
}
/// Build client to use in connection.
async fn build_client(params: ConnectionParams) -> Result<RpcClient> {
let uri = format!("ws://{}:{}", params.host, params.port);
let mut config = RpcConfig::with_url(&uri);
config.max_subscription_capacity = MAX_SUBSCRIPTION_CAPACITY;
let client = RpcClient::new(config).await?;
Ok(client)
}
}
impl<C: Chain> Client<C> {
/// Returns true if client is connected to at least one peer and is in synced state.
pub async fn ensure_synced(&self) -> Result<()> {
let health = Substrate::<C>::system_health(&self.client).await?;
let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0);
if is_synced {
Ok(())
} else {
Err(Error::ClientNotSynced(health))
}
}
/// Return hash of the genesis block.
pub fn genesis_hash(&self) -> &C::Hash {
&self.genesis_hash
}
/// Return hash of the best finalized block.
pub async fn best_finalized_header_hash(&self) -> Result<C::Hash> {
Ok(Substrate::<C>::chain_get_finalized_head(&self.client).await?)
}
/// Returns the best Substrate header.
pub async fn best_header(&self) -> Result<C::Header>
where
C::Header: DeserializeOwned,
{
Ok(Substrate::<C>::chain_get_header(&self.client, None).await?)
}
/// Get a Substrate block from its hash.
pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> {
Ok(Substrate::<C>::chain_get_block(&self.client, block_hash).await?)
}
/// Get a Substrate header by its hash.
pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result<C::Header>
where
C::Header: DeserializeOwned,
{
Ok(Substrate::<C>::chain_get_header(&self.client, block_hash).await?)
}
/// Get a Substrate block hash by its number.
pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result<C::Hash> {
Ok(Substrate::<C>::chain_get_block_hash(&self.client, number).await?)
}
/// Get a Substrate header by its number.
pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result<C::Header>
where
C::Header: DeserializeOwned,
{
let block_hash = Self::block_hash_by_number(self, block_number).await?;
Ok(Self::header_by_hash(self, block_hash).await?)
}
/// Return runtime version.
pub async fn runtime_version(&self) -> Result<RuntimeVersion> {
Ok(Substrate::<C>::runtime_version(&self.client).await?)
}
/// Return native tokens balance of the account.
pub async fn free_native_balance(&self, account: C::AccountId) -> Result<C::NativeBalance>
where
C: ChainWithBalances,
{
let storage_key = C::account_info_storage_key(&account);
let encoded_account_data = Substrate::<C>::get_storage(&self.client, storage_key)
.await?
.ok_or(Error::AccountDoesNotExist)?;
let decoded_account_data =
AccountInfo::<C::Index, AccountData<C::NativeBalance>>::decode(&mut &encoded_account_data.0[..])
.map_err(Error::ResponseParseFailed)?;
Ok(decoded_account_data.data.free)
}
/// Get the nonce of the given Substrate account.
///
/// Note: It's the caller's responsibility to make sure `account` is a valid ss58 address.
pub async fn next_account_index(&self, account: C::AccountId) -> Result<C::Index> {
Ok(Substrate::<C>::system_account_next_index(&self.client, account).await?)
}
/// Submit an extrinsic for inclusion in a block.
///
/// Note: The given transaction does not need be SCALE encoded beforehand.
pub async fn submit_extrinsic(&self, transaction: Bytes) -> Result<C::Hash> {
let tx_hash = Substrate::<C>::author_submit_extrinsic(&self.client, transaction).await?;
log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash);
Ok(tx_hash)
}
/// Get the GRANDPA authority set at given block.
pub async fn grandpa_authorities_set(&self, block: C::Hash) -> Result<OpaqueGrandpaAuthoritiesSet> {
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
let data = Bytes(Vec::new());
let encoded_response = Substrate::<C>::state_call(&self.client, call, data, Some(block)).await?;
let authority_list = encoded_response.0;
Ok(authority_list)
}
/// Execute runtime call at given block.
pub async fn state_call(&self, method: String, data: Bytes, at_block: Option<C::Hash>) -> Result<Bytes> {
Substrate::<C>::state_call(&self.client, method, data, at_block)
.await
.map_err(Into::into)
}
/// Returns proof-of-message(s) in given inclusive range.
pub async fn prove_messages(
&self,
instance: InstanceId,
lane: LaneId,
range: RangeInclusive<MessageNonce>,
include_outbound_lane_state: bool,
at_block: C::Hash,
) -> Result<StorageProof> {
let encoded_trie_nodes = SubstrateMessageLane::<C>::prove_messages(
&self.client,
instance,
lane,
*range.start(),
*range.end(),
include_outbound_lane_state,
Some(at_block),
)
.await
.map_err(Error::RpcError)?;
let decoded_trie_nodes: Vec<Vec<u8>> =
Decode::decode(&mut &encoded_trie_nodes[..]).map_err(Error::ResponseParseFailed)?;
Ok(StorageProof::new(decoded_trie_nodes))
}
/// Returns proof-of-message(s) delivery.
pub async fn prove_messages_delivery(
&self,
instance: InstanceId,
lane: LaneId,
at_block: C::Hash,
) -> Result<Vec<Vec<u8>>> {
let encoded_trie_nodes =
SubstrateMessageLane::<C>::prove_messages_delivery(&self.client, instance, lane, Some(at_block))
.await
.map_err(Error::RpcError)?;
let decoded_trie_nodes: Vec<Vec<u8>> =
Decode::decode(&mut &encoded_trie_nodes[..]).map_err(Error::ResponseParseFailed)?;
Ok(decoded_trie_nodes)
}
/// Return new justifications stream.
pub async fn subscribe_justifications(&self) -> Result<JustificationsSubscription> {
Ok(self
.client
.subscribe(
"grandpa_subscribeJustifications",
jsonrpsee_types::jsonrpc::Params::None,
"grandpa_unsubscribeJustifications",
)
.await?)
}
}
@@ -0,0 +1,81 @@
// 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 <http://www.gnu.org/licenses/>.
//! Substrate node RPC errors.
use jsonrpsee_types::error::Error as RpcError;
use relay_utils::MaybeConnectionError;
use sc_rpc_api::system::Health;
/// Result type used by Substrate client.
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can occur only when interacting with
/// a Substrate node through RPC.
#[derive(Debug)]
pub enum Error {
/// An error that can occur when making a request to
/// an JSON-RPC server.
RpcError(RpcError),
/// The response from the server could not be SCALE decoded.
ResponseParseFailed(codec::Error),
/// The Substrate bridge pallet has not yet been initialized.
UninitializedBridgePallet,
/// Account does not exist on the chain.
AccountDoesNotExist,
/// The client we're connected to is not synced, so we can't rely on its state.
ClientNotSynced(Health),
/// Custom logic error.
Custom(String),
}
impl From<RpcError> for Error {
fn from(error: RpcError) -> Self {
Error::RpcError(error)
}
}
impl MaybeConnectionError for Error {
fn is_connection_error(&self) -> bool {
matches!(
*self,
Error::RpcError(RpcError::TransportError(_))
// right now if connection to the ws server is dropped (after it is already established),
// we're getting this error
| Error::RpcError(RpcError::Internal(_))
| Error::ClientNotSynced(_),
)
}
}
impl From<Error> for String {
fn from(error: Error) -> String {
error.to_string()
}
}
impl ToString for Error {
fn to_string(&self) -> String {
match self {
Self::RpcError(e) => e.to_string(),
Self::ResponseParseFailed(e) => e.to_string(),
Self::UninitializedBridgePallet => "The Substrate bridge pallet has not been initialized yet.".into(),
Self::AccountDoesNotExist => "Account does not exist on the chain".into(),
Self::ClientNotSynced(health) => format!("Substrate client is not synced: {}", health),
Self::Custom(e) => e.clone(),
}
}
}
@@ -0,0 +1,162 @@
// 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 <http://www.gnu.org/licenses/>.
//! Default generic implementation of finality source for basic Substrate client.
use crate::chain::{BlockWithJustification, Chain};
use crate::client::Client;
use crate::error::Error;
use crate::sync_header::SyncHeader;
use async_trait::async_trait;
use bp_header_chain::justification::decode_justification_target;
use finality_relay::{FinalityProof, FinalitySyncPipeline, SourceClient, SourceHeader};
use futures::stream::{unfold, Stream, StreamExt};
use relay_utils::relay_loop::Client as RelayClient;
use sp_runtime::traits::Header as HeaderT;
use std::{marker::PhantomData, pin::Pin};
/// Wrapped raw Justification.
#[derive(Debug, Clone)]
pub struct Justification<Number> {
/// Header number decoded from the [`raw_justification`].
target_header_number: Number,
/// Raw, encoded justification bytes.
raw_justification: sp_runtime::Justification,
}
impl<Number> Justification<Number> {
/// Extract raw justification.
pub fn into_inner(self) -> sp_runtime::Justification {
self.raw_justification
}
}
impl<Number: relay_utils::BlockNumberBase> FinalityProof<Number> for Justification<Number> {
fn target_header_number(&self) -> Number {
self.target_header_number
}
}
/// Substrate node as finality source.
pub struct FinalitySource<C: Chain, P> {
client: Client<C>,
_phantom: PhantomData<P>,
}
impl<C: Chain, P> FinalitySource<C, P> {
/// Create new headers source using given client.
pub fn new(client: Client<C>) -> Self {
FinalitySource {
client,
_phantom: Default::default(),
}
}
}
impl<C: Chain, P> Clone for FinalitySource<C, P> {
fn clone(&self) -> Self {
FinalitySource {
client: self.client.clone(),
_phantom: Default::default(),
}
}
}
#[async_trait]
impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> {
type Error = Error;
async fn reconnect(&mut self) -> Result<(), Error> {
self.client.reconnect().await
}
}
#[async_trait]
impl<C, P> SourceClient<P> for FinalitySource<C, P>
where
C: Chain,
C::BlockNumber: relay_utils::BlockNumberBase,
P: FinalitySyncPipeline<
Hash = C::Hash,
Number = C::BlockNumber,
Header = SyncHeader<C::Header>,
FinalityProof = Justification<C::BlockNumber>,
>,
P::Header: SourceHeader<C::BlockNumber>,
{
type FinalityProofsStream = Pin<Box<dyn Stream<Item = Justification<C::BlockNumber>>>>;
async fn best_finalized_block_number(&self) -> Result<P::Number, Error> {
// we **CAN** continue to relay finality proofs if source node is out of sync, because
// target node may be missing proofs that are already available at the source
let finalized_header_hash = self.client.best_finalized_header_hash().await?;
let finalized_header = self.client.header_by_hash(finalized_header_hash).await?;
Ok(*finalized_header.number())
}
async fn header_and_finality_proof(
&self,
number: P::Number,
) -> Result<(P::Header, Option<P::FinalityProof>), Error> {
let header_hash = self.client.block_hash_by_number(number).await?;
let signed_block = self.client.get_block(Some(header_hash)).await?;
Ok((
signed_block.header().into(),
signed_block
.justification()
.cloned()
.map(|raw_justification| Justification {
target_header_number: number,
raw_justification,
}),
))
}
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
Ok(unfold(
self.client.clone().subscribe_justifications().await?,
move |mut subscription| async move {
loop {
let next_justification = subscription.next().await?;
let decoded_target = decode_justification_target::<C::Header>(&next_justification.0);
let target_header_number = match decoded_target {
Ok((_, number)) => number,
Err(err) => {
log::error!(
target: "bridge",
"Failed to decode justification target from the {} justifications stream: {:?}",
P::SOURCE_NAME,
err,
);
continue;
}
};
return Some((
Justification {
target_header_number,
raw_justification: next_justification.0,
},
subscription,
));
}
},
)
.boxed())
}
}
@@ -0,0 +1,372 @@
// 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 <http://www.gnu.org/licenses/>.
//! Module provides a set of guard functions that are running in background threads
//! and are aborting process if some condition fails.
use crate::{Chain, ChainWithBalances, Client};
use async_trait::async_trait;
use num_traits::CheckedSub;
use sp_version::RuntimeVersion;
use std::{
collections::VecDeque,
time::{Duration, Instant},
};
/// Guards environment.
#[async_trait]
pub trait Environment<C: ChainWithBalances>: Send + Sync + 'static {
/// Return current runtime version.
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String>;
/// Return free native balance of the account on the chain.
async fn free_native_balance(&mut self, account: C::AccountId) -> Result<C::NativeBalance, String>;
/// Return current time.
fn now(&self) -> Instant {
Instant::now()
}
/// Sleep given amount of time.
async fn sleep(&mut self, duration: Duration) {
async_std::task::sleep(duration).await
}
/// Abort current process. Called when guard condition check fails.
async fn abort(&mut self) {
std::process::abort();
}
}
/// Abort when runtime spec version is different from specified.
pub fn abort_on_spec_version_change<C: ChainWithBalances>(mut env: impl Environment<C>, expected_spec_version: u32) {
async_std::task::spawn(async move {
loop {
let actual_spec_version = env.runtime_version().await;
match actual_spec_version {
Ok(version) if version.spec_version == expected_spec_version => (),
Ok(version) => {
log::error!(
target: "bridge-guard",
"{} runtime spec version has changed from {} to {}. Aborting relay",
C::NAME,
expected_spec_version,
version.spec_version,
);
env.abort().await;
}
Err(error) => log::warn!(
target: "bridge-guard",
"Failed to read {} runtime version: {:?}. Relay may need to be stopped manually",
C::NAME,
error,
),
}
env.sleep(conditions_check_delay::<C>()).await;
}
});
}
/// Abort if, during a 24 hours, free balance of given account is decreased at least by given value.
/// Other components may increase (or decrease) balance of account and it WILL affect logic of the guard.
pub fn abort_when_account_balance_decreased<C: ChainWithBalances>(
mut env: impl Environment<C>,
account_id: C::AccountId,
maximal_decrease: C::NativeBalance,
) {
const DAY: Duration = Duration::from_secs(60 * 60 * 24);
async_std::task::spawn(async move {
let mut balances = VecDeque::new();
loop {
let current_time = env.now();
// remember balances that are beyound 24h border
let time_border = current_time - DAY;
while balances.front().map(|(time, _)| *time < time_border).unwrap_or(false) {
balances.pop_front();
}
// read balance of the account
let current_balance = env.free_native_balance(account_id.clone()).await;
// remember balance and check difference
match current_balance {
Ok(current_balance) => {
// remember balance
balances.push_back((current_time, current_balance));
// check if difference between current and oldest balance is too large
let (oldest_time, oldest_balance) =
balances.front().expect("pushed to queue couple of lines above; qed");
let balances_difference = oldest_balance.checked_sub(&current_balance);
if balances_difference > Some(maximal_decrease) {
log::error!(
target: "bridge-guard",
"Balance of {} account {:?} has decreased from {:?} to {:?} in {} minutes. Aborting relay",
C::NAME,
account_id,
oldest_balance,
current_balance,
current_time.duration_since(*oldest_time).as_secs() / 60,
);
env.abort().await;
}
}
Err(error) => {
log::warn!(
target: "bridge-guard",
"Failed to read {} account {:?} balance: {:?}. Relay may need to be stopped manually",
C::NAME,
account_id,
error,
);
}
};
env.sleep(conditions_check_delay::<C>()).await;
}
});
}
/// Delay between conditions check.
fn conditions_check_delay<C: Chain>() -> Duration {
C::AVERAGE_BLOCK_INTERVAL * (10 + rand::random::<u32>() % 10)
}
#[async_trait]
impl<C: ChainWithBalances> Environment<C> for Client<C> {
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String> {
Client::<C>::runtime_version(self).await.map_err(|e| e.to_string())
}
async fn free_native_balance(&mut self, account: C::AccountId) -> Result<C::NativeBalance, String> {
Client::<C>::free_native_balance(self, account)
.await
.map_err(|e| e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::{
channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
future::FutureExt,
stream::StreamExt,
SinkExt,
};
struct TestChain;
impl bp_runtime::Chain for TestChain {
type BlockNumber = u32;
type Hash = sp_core::H256;
type Hasher = sp_runtime::traits::BlakeTwo256;
type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;
}
impl Chain for TestChain {
const NAME: &'static str = "Test";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(1);
type AccountId = u32;
type Index = u32;
type SignedBlock =
sp_runtime::generic::SignedBlock<sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>>;
type Call = ();
}
impl ChainWithBalances for TestChain {
type NativeBalance = u32;
fn account_info_storage_key(_account_id: &u32) -> sp_core::storage::StorageKey {
unreachable!()
}
}
struct TestEnvironment {
runtime_version_rx: UnboundedReceiver<RuntimeVersion>,
free_native_balance_rx: UnboundedReceiver<u32>,
slept_tx: UnboundedSender<()>,
aborted_tx: UnboundedSender<()>,
}
#[async_trait]
impl Environment<TestChain> for TestEnvironment {
async fn runtime_version(&mut self) -> Result<RuntimeVersion, String> {
Ok(self.runtime_version_rx.next().await.unwrap_or_default())
}
async fn free_native_balance(&mut self, _account: u32) -> Result<u32, String> {
Ok(self.free_native_balance_rx.next().await.unwrap_or_default())
}
async fn sleep(&mut self, _duration: Duration) {
let _ = self.slept_tx.send(()).await;
}
async fn abort(&mut self) {
let _ = self.aborted_tx.send(()).await;
// simulate process abort :)
async_std::task::sleep(Duration::from_secs(60)).await;
}
}
#[test]
fn aborts_when_spec_version_is_changed() {
async_std::task::block_on(async {
let (
(mut runtime_version_tx, runtime_version_rx),
(_free_native_balance_tx, free_native_balance_rx),
(slept_tx, mut slept_rx),
(aborted_tx, mut aborted_rx),
) = (unbounded(), unbounded(), unbounded(), unbounded());
abort_on_spec_version_change(
TestEnvironment {
runtime_version_rx,
free_native_balance_rx,
slept_tx,
aborted_tx,
},
0,
);
// client responds with wrong version
runtime_version_tx
.send(RuntimeVersion {
spec_version: 42,
..Default::default()
})
.await
.unwrap();
// then the `abort` function is called
aborted_rx.next().await;
// and we do not reach the `sleep` function call
assert!(slept_rx.next().now_or_never().is_none());
});
}
#[test]
fn does_not_aborts_when_spec_version_is_unchanged() {
async_std::task::block_on(async {
let (
(mut runtime_version_tx, runtime_version_rx),
(_free_native_balance_tx, free_native_balance_rx),
(slept_tx, mut slept_rx),
(aborted_tx, mut aborted_rx),
) = (unbounded(), unbounded(), unbounded(), unbounded());
abort_on_spec_version_change(
TestEnvironment {
runtime_version_rx,
free_native_balance_rx,
slept_tx,
aborted_tx,
},
42,
);
// client responds with the same version
runtime_version_tx
.send(RuntimeVersion {
spec_version: 42,
..Default::default()
})
.await
.unwrap();
// then the `sleep` function is called
slept_rx.next().await;
// and the `abort` function is not called
assert!(aborted_rx.next().now_or_never().is_none());
});
}
#[test]
fn aborts_when_balance_is_too_low() {
async_std::task::block_on(async {
let (
(_runtime_version_tx, runtime_version_rx),
(mut free_native_balance_tx, free_native_balance_rx),
(slept_tx, mut slept_rx),
(aborted_tx, mut aborted_rx),
) = (unbounded(), unbounded(), unbounded(), unbounded());
abort_when_account_balance_decreased(
TestEnvironment {
runtime_version_rx,
free_native_balance_rx,
slept_tx,
aborted_tx,
},
0,
100,
);
// client responds with initial balance
free_native_balance_tx.send(1000).await.unwrap();
// then the guard sleeps
slept_rx.next().await;
// and then client responds with updated balance, which is too low
free_native_balance_tx.send(899).await.unwrap();
// then the `abort` function is called
aborted_rx.next().await;
// and we do not reach next `sleep` function call
assert!(slept_rx.next().now_or_never().is_none());
});
}
#[test]
fn does_not_aborts_when_balance_is_enough() {
async_std::task::block_on(async {
let (
(_runtime_version_tx, runtime_version_rx),
(mut free_native_balance_tx, free_native_balance_rx),
(slept_tx, mut slept_rx),
(aborted_tx, mut aborted_rx),
) = (unbounded(), unbounded(), unbounded(), unbounded());
abort_when_account_balance_decreased(
TestEnvironment {
runtime_version_rx,
free_native_balance_rx,
slept_tx,
aborted_tx,
},
0,
100,
);
// client responds with initial balance
free_native_balance_tx.send(1000).await.unwrap();
// then the guard sleeps
slept_rx.next().await;
// and then client responds with updated balance, which is enough
free_native_balance_tx.send(950).await.unwrap();
// then the `sleep` function is called
slept_rx.next().await;
// and `abort` is not called
assert!(aborted_rx.next().now_or_never().is_none());
});
}
}
@@ -0,0 +1,108 @@
// 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 <http://www.gnu.org/licenses/>.
//! Default generic implementation of headers source for basic Substrate client.
use crate::chain::{BlockWithJustification, Chain};
use crate::client::Client;
use crate::error::Error;
use async_trait::async_trait;
use headers_relay::{
sync_loop::SourceClient,
sync_types::{HeaderIdOf, HeadersSyncPipeline, QueuedHeader, SourceHeader},
};
use relay_utils::relay_loop::Client as RelayClient;
use sp_runtime::{traits::Header as HeaderT, Justification};
use std::marker::PhantomData;
/// Substrate node as headers source.
pub struct HeadersSource<C: Chain, P> {
client: Client<C>,
_phantom: PhantomData<P>,
}
impl<C: Chain, P> HeadersSource<C, P> {
/// Create new headers source using given client.
pub fn new(client: Client<C>) -> Self {
HeadersSource {
client,
_phantom: Default::default(),
}
}
}
impl<C: Chain, P> Clone for HeadersSource<C, P> {
fn clone(&self) -> Self {
HeadersSource {
client: self.client.clone(),
_phantom: Default::default(),
}
}
}
#[async_trait]
impl<C: Chain, P: HeadersSyncPipeline> RelayClient for HeadersSource<C, P> {
type Error = Error;
async fn reconnect(&mut self) -> Result<(), Error> {
self.client.reconnect().await
}
}
#[async_trait]
impl<C, P> SourceClient<P> for HeadersSource<C, P>
where
C: Chain,
C::BlockNumber: relay_utils::BlockNumberBase,
C::Header: Into<P::Header>,
P: HeadersSyncPipeline<Extra = (), Completion = Justification, Hash = C::Hash, Number = C::BlockNumber>,
P::Header: SourceHeader<C::Hash, C::BlockNumber>,
{
async fn best_block_number(&self) -> Result<P::Number, Error> {
// we **CAN** continue to relay headers if source node is out of sync, because
// target node may be missing headers that are already available at the source
Ok(*self.client.best_header().await?.number())
}
async fn header_by_hash(&self, hash: P::Hash) -> Result<P::Header, Error> {
self.client
.header_by_hash(hash)
.await
.map(Into::into)
.map_err(Into::into)
}
async fn header_by_number(&self, number: P::Number) -> Result<P::Header, Error> {
self.client
.header_by_number(number)
.await
.map(Into::into)
.map_err(Into::into)
}
async fn header_completion(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, Option<P::Completion>), Error> {
let hash = id.1;
let signed_block = self.client.get_block(Some(hash)).await?;
let grandpa_justification = signed_block.justification().cloned();
Ok((id, grandpa_justification))
}
async fn header_extra(&self, id: HeaderIdOf<P>, _header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, ()), Error> {
Ok((id, ()))
}
}
@@ -0,0 +1,56 @@
// 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 <http://www.gnu.org/licenses/>.
//! Tools to interact with (Open) Ethereum node using RPC methods.
#![warn(missing_docs)]
mod chain;
mod client;
mod error;
mod rpc;
mod sync_header;
pub mod finality_source;
pub mod guard;
pub mod headers_source;
pub use crate::chain::{BlockWithJustification, Chain, ChainWithBalances, TransactionSignScheme};
pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthoritiesSet};
pub use crate::error::{Error, Result};
pub use crate::sync_header::SyncHeader;
pub use bp_runtime::{BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf};
/// Header id used by the chain.
pub type HeaderIdOf<C> = relay_utils::HeaderId<HashOf<C>, BlockNumberOf<C>>;
/// Substrate-over-websocket connection params.
#[derive(Debug, Clone)]
pub struct ConnectionParams {
/// Websocket server hostname.
pub host: String,
/// Websocket server TCP port.
pub port: u16,
}
impl Default for ConnectionParams {
fn default() -> Self {
ConnectionParams {
host: "localhost".into(),
port: 9944,
}
}
}
@@ -0,0 +1,77 @@
// 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 <http://www.gnu.org/licenses/>.
//! The most generic Substrate node RPC interface.
// The compiler doesn't think we're using the
// code from rpc_api!
#![allow(dead_code)]
#![allow(unused_variables)]
use crate::chain::Chain;
use bp_message_lane::{LaneId, MessageNonce};
use bp_runtime::InstanceId;
use sc_rpc_api::system::Health;
use sp_core::{
storage::{StorageData, StorageKey},
Bytes,
};
use sp_version::RuntimeVersion;
jsonrpsee_proc_macros::rpc_client_api! {
pub(crate) Substrate<C: Chain> {
#[rpc(method = "system_health", positional_params)]
fn system_health() -> Health;
#[rpc(method = "chain_getHeader", positional_params)]
fn chain_get_header(block_hash: Option<C::Hash>) -> C::Header;
#[rpc(method = "chain_getFinalizedHead", positional_params)]
fn chain_get_finalized_head() -> C::Hash;
#[rpc(method = "chain_getBlock", positional_params)]
fn chain_get_block(block_hash: Option<C::Hash>) -> C::SignedBlock;
#[rpc(method = "chain_getBlockHash", positional_params)]
fn chain_get_block_hash(block_number: Option<C::BlockNumber>) -> C::Hash;
#[rpc(method = "system_accountNextIndex", positional_params)]
fn system_account_next_index(account_id: C::AccountId) -> C::Index;
#[rpc(method = "author_submitExtrinsic", positional_params)]
fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash;
#[rpc(method = "state_call", positional_params)]
fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes;
#[rpc(method = "state_getStorage", positional_params)]
fn get_storage(key: StorageKey) -> Option<StorageData>;
#[rpc(method = "state_getRuntimeVersion", positional_params)]
fn runtime_version() -> RuntimeVersion;
}
pub(crate) SubstrateMessageLane<C: Chain> {
#[rpc(method = "messageLane_proveMessages", positional_params)]
fn prove_messages(
instance: InstanceId,
lane: LaneId,
begin: MessageNonce,
end: MessageNonce,
include_outbound_lane_state: bool,
block: Option<C::Hash>,
) -> Bytes;
#[rpc(method = "messageLane_proveMessagesDelivery", positional_params)]
fn prove_messages_delivery(
instance: InstanceId,
lane: LaneId,
block: Option<C::Hash>,
) -> Bytes;
}
}
@@ -0,0 +1,73 @@
// 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 <http://www.gnu.org/licenses/>.
use bp_header_chain::find_grandpa_authorities_scheduled_change;
use finality_relay::SourceHeader as FinalitySourceHeader;
use headers_relay::sync_types::SourceHeader;
use num_traits::{CheckedSub, One};
use relay_utils::HeaderId;
use sp_runtime::traits::Header as HeaderT;
/// Generic wrapper for `sp_runtime::traits::Header` based headers, that
/// implements `headers_relay::sync_types::SourceHeader` and may be used in headers sync directly.
#[derive(Clone, Debug, PartialEq)]
pub struct SyncHeader<Header>(Header);
impl<Header> SyncHeader<Header> {
/// Extracts wrapped header from self.
pub fn into_inner(self) -> Header {
self.0
}
}
impl<Header> std::ops::Deref for SyncHeader<Header> {
type Target = Header;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<Header> From<Header> for SyncHeader<Header> {
fn from(header: Header) -> Self {
Self(header)
}
}
impl<Header: HeaderT> SourceHeader<Header::Hash, Header::Number> for SyncHeader<Header> {
fn id(&self) -> HeaderId<Header::Hash, Header::Number> {
relay_utils::HeaderId(*self.0.number(), self.hash())
}
fn parent_id(&self) -> HeaderId<Header::Hash, Header::Number> {
relay_utils::HeaderId(
self.number()
.checked_sub(&One::one())
.expect("should never be called for genesis header"),
*self.parent_hash(),
)
}
}
impl<Header: HeaderT> FinalitySourceHeader<Header::Number> for SyncHeader<Header> {
fn number(&self) -> Header::Number {
*self.0.number()
}
fn is_mandatory(&self) -> bool {
find_grandpa_authorities_scheduled_change(&self.0).is_some()
}
}