Match substrate's fmt (#1148)

* Alter gitlab.

* Use substrate's rustfmt.toml

* cargo +nightly fmt --all

* Fix spellcheck.

* cargo +nightly fmt --all

* format.

* Fix spellcheck and fmt

* fmt?

* Fix spellcheck

Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
This commit is contained in:
hacpy
2021-09-24 19:29:31 +08:00
committed by Bastian Köcher
parent 87cbb382d9
commit bd70de8b8b
174 changed files with 6095 additions and 4962 deletions
@@ -14,8 +14,7 @@
// 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_errors::RpcError;
use crate::substrate_sync_loop::QueuedRialtoHeader;
use crate::{rpc_errors::RpcError, substrate_sync_loop::QueuedRialtoHeader};
use async_trait::async_trait;
use bp_eth_poa::signatures::secret_to_address;
@@ -60,7 +59,10 @@ pub trait EthereumHighLevelRpc {
) -> SubmittedHeaders<RialtoHeaderId, RpcError>;
/// Returns ids of incomplete Substrate headers.
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>>;
async fn incomplete_substrate_headers(
&self,
contract_address: Address,
) -> RpcResult<HashSet<RialtoHeaderId>>;
/// Complete Substrate header.
async fn complete_substrate_header(
@@ -104,7 +106,7 @@ impl EthereumHighLevelRpc for EthereumClient {
let hash = rialto_runtime::Hash::decode(&mut &raw_hash[..])?;
if number != number.low_u32().into() {
return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber));
return Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber))
}
Ok(HeaderId(number.low_u32(), hash))
@@ -138,31 +140,28 @@ impl EthereumHighLevelRpc for EthereumClient {
let address: Address = secret_to_address(&params.signer);
let nonce = match self.account_nonce(address).await {
Ok(nonce) => nonce,
Err(error) => {
Err(error) =>
return SubmittedHeaders {
submitted: Vec::new(),
incomplete: Vec::new(),
rejected: headers.iter().rev().map(|header| header.id()).collect(),
fatal_error: Some(error.into()),
}
}
},
};
// submit headers. Note that we're cloning self here. It is ok, because
// cloning `jsonrpsee::Client` only clones reference to background threads
submit_substrate_headers(
EthereumHeadersSubmitter {
client: self.clone(),
params,
contract_address,
nonce,
},
EthereumHeadersSubmitter { client: self.clone(), params, contract_address, nonce },
headers,
)
.await
}
async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult<HashSet<RialtoHeaderId>> {
async fn incomplete_substrate_headers(
&self,
contract_address: Address,
) -> RpcResult<HashSet<RialtoHeaderId>> {
let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call();
let call_request = CallRequest {
to: Some(contract_address),
@@ -173,13 +172,14 @@ impl EthereumHighLevelRpc for EthereumClient {
let call_result = self.eth_call(call_request).await?;
// Q: Is is correct to call these "incomplete_ids"?
let (incomplete_headers_numbers, incomplete_headers_hashes) = call_decoder.decode(&call_result.0)?;
let (incomplete_headers_numbers, incomplete_headers_hashes) =
call_decoder.decode(&call_result.0)?;
let incomplete_ids = incomplete_headers_numbers
.into_iter()
.zip(incomplete_headers_hashes)
.filter_map(|(number, hash)| {
if number != number.low_u32().into() {
return None;
return None
}
Some(HeaderId(number.low_u32(), hash))
@@ -202,7 +202,11 @@ impl EthereumHighLevelRpc for EthereumClient {
Some(contract_address),
None,
false,
bridge_contract::functions::import_finality_proof::encode_input(id.0, id.1, justification),
bridge_contract::functions::import_finality_proof::encode_input(
id.0,
id.1,
justification,
),
)
.await?;
@@ -263,7 +267,7 @@ impl HeadersBatch {
) -> Result<(Self, Vec<RialtoHeaderId>), ()> {
if headers.len() != ids.len() {
log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len());
return Err(());
return Err(())
}
let header1 = headers.pop().ok_or(())?;
@@ -276,27 +280,14 @@ impl HeadersBatch {
submitting_ids.extend(ids.pop().iter());
}
Ok((
Self {
header1,
header2,
header3,
header4,
},
submitting_ids,
))
Ok((Self { header1, header2, header3, header4 }, submitting_ids))
}
/// Returns unified array of headers.
///
/// The first element is always `Some`.
fn headers(&self) -> [Option<&QueuedRialtoHeader>; HEADERS_BATCH] {
[
Some(&self.header1),
self.header2.as_ref(),
self.header3.as_ref(),
self.header4.as_ref(),
]
[Some(&self.header1), self.header2.as_ref(), self.header3.as_ref(), self.header4.as_ref()]
}
/// Encodes all headers. If header is not present an empty vector will be returned.
@@ -323,9 +314,10 @@ impl HeadersBatch {
/// or when `idx > HEADERS_BATCH`.
pub fn split_off(&mut self, idx: usize) -> Result<(), ()> {
if idx == 0 || idx > HEADERS_BATCH {
return Err(());
return Err(())
}
let mut vals: [_; HEADERS_BATCH] = [&mut None, &mut self.header2, &mut self.header3, &mut self.header4];
let mut vals: [_; HEADERS_BATCH] =
[&mut None, &mut self.header2, &mut self.header3, &mut self.header4];
for val in vals.iter_mut().skip(idx) {
**val = None;
}
@@ -359,7 +351,8 @@ struct EthereumHeadersSubmitter {
impl HeadersSubmitter for EthereumHeadersSubmitter {
async fn is_headers_incomplete(&self, headers: &HeadersBatch) -> RpcResult<usize> {
let [h1, h2, h3, h4] = headers.encode();
let (encoded_call, call_decoder) = bridge_contract::functions::is_incomplete_headers::call(h1, h2, h3, h4);
let (encoded_call, call_decoder) =
bridge_contract::functions::is_incomplete_headers::call(h1, h2, h3, h4);
let call_request = CallRequest {
to: Some(self.contract_address),
data: Some(encoded_call.into()),
@@ -369,7 +362,7 @@ impl HeadersSubmitter for EthereumHeadersSubmitter {
let call_result = self.client.eth_call(call_request).await?;
let incomplete_index: U256 = call_decoder.decode(&call_result.0)?;
if incomplete_index > HEADERS_BATCH.into() {
return Err(RpcError::Ethereum(EthereumNodeError::InvalidIncompleteIndex));
return Err(RpcError::Ethereum(EthereumNodeError::InvalidIncompleteIndex))
}
Ok(incomplete_index.low_u32() as _)
@@ -407,17 +400,21 @@ async fn submit_substrate_headers(
headers.reverse();
while !headers.is_empty() {
let (headers, submitting_ids) =
HeadersBatch::pop_from(&mut headers, &mut ids).expect("Headers and ids are not empty; qed");
let (headers, submitting_ids) = HeadersBatch::pop_from(&mut headers, &mut ids)
.expect("Headers and ids are not empty; qed");
submitted_headers.fatal_error =
submit_substrate_headers_batch(&mut header_submitter, &mut submitted_headers, submitting_ids, headers)
.await;
submitted_headers.fatal_error = submit_substrate_headers_batch(
&mut header_submitter,
&mut submitted_headers,
submitting_ids,
headers,
)
.await;
if submitted_headers.fatal_error.is_some() {
ids.reverse();
submitted_headers.rejected.extend(ids);
break;
break
}
}
@@ -436,9 +433,11 @@ async fn submit_substrate_headers_batch(
// if parent of first header is either incomplete, or rejected, we assume that contract
// will reject this header as well
let parent_id = headers.header1.parent_id();
if submitted_headers.rejected.contains(&parent_id) || submitted_headers.incomplete.contains(&parent_id) {
if submitted_headers.rejected.contains(&parent_id) ||
submitted_headers.incomplete.contains(&parent_id)
{
submitted_headers.rejected.extend(ids);
return None;
return None
}
// check if headers are incomplete
@@ -450,11 +449,11 @@ async fn submit_substrate_headers_batch(
// contract has rejected all headers => we do not want to submit it
submitted_headers.rejected.extend(ids);
if error.is_connection_error() {
return Some(error);
return Some(error)
} else {
return None;
return None
}
}
},
};
// Modify `ids` and `headers` to only contain values that are going to be accepted.
@@ -477,12 +476,12 @@ async fn submit_substrate_headers_batch(
submitted_headers.submitted.extend(submitted);
submitted_headers.rejected.extend(rejected);
None
}
},
Err(error) => {
submitted_headers.rejected.extend(submitted);
submitted_headers.rejected.extend(rejected);
Some(error)
}
},
}
}
@@ -521,11 +520,7 @@ mod tests {
number,
Default::default(),
Default::default(),
if number == 0 {
Default::default()
} else {
header(number - 1).id().1
},
if number == 0 { Default::default() } else { header(number - 1).id().1 },
Default::default(),
)
.into(),
@@ -535,10 +530,7 @@ mod tests {
#[test]
fn descendants_of_incomplete_headers_are_not_submitted() {
let submitted_headers = async_std::task::block_on(submit_substrate_headers(
TestHeadersSubmitter {
incomplete: vec![header(5).id()],
failed: vec![],
},
TestHeadersSubmitter { incomplete: vec![header(5).id()], failed: vec![] },
vec![header(5), header(6)],
));
assert_eq!(submitted_headers.submitted, vec![header(5).id()]);
@@ -550,19 +542,8 @@ mod tests {
#[test]
fn headers_after_fatal_error_are_not_submitted() {
let submitted_headers = async_std::task::block_on(submit_substrate_headers(
TestHeadersSubmitter {
incomplete: vec![],
failed: vec![header(9).id()],
},
vec![
header(5),
header(6),
header(7),
header(8),
header(9),
header(10),
header(11),
],
TestHeadersSubmitter { incomplete: vec![], failed: vec![header(9).id()] },
vec![header(5), header(6), header(7), header(8), header(9), header(10), header(11)],
));
assert_eq!(
submitted_headers.submitted,
@@ -583,10 +564,7 @@ mod tests {
let (headers, ids) = HeadersBatch::pop_from(&mut init_headers, &mut init_ids).unwrap();
assert_eq!(init_headers, vec![header(5)]);
assert_eq!(init_ids, vec![header(5).id()]);
assert_eq!(
ids,
vec![header(1).id(), header(2).id(), header(3).id(), header(4).id()]
);
assert_eq!(ids, vec![header(1).id(), header(2).id(), header(3).id(), header(4).id()]);
headers
}
@@ -14,17 +14,21 @@
// 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::ethereum_client::{bridge_contract, EthereumHighLevelRpc};
use crate::rpc_errors::RpcError;
use crate::{
ethereum_client::{bridge_contract, EthereumHighLevelRpc},
rpc_errors::RpcError,
};
use codec::{Decode, Encode};
use num_traits::Zero;
use relay_ethereum_client::{
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
SigningParams as EthereumSigningParams,
};
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto};
use relay_substrate_client::{
Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams, OpaqueGrandpaAuthoritiesSet,
Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams,
OpaqueGrandpaAuthoritiesSet,
};
use relay_utils::HeaderId;
@@ -102,19 +106,18 @@ async fn prepare_initial_header(
sub_initial_header: Option<Vec<u8>>,
) -> Result<(RialtoHeaderId, Vec<u8>), String> {
match sub_initial_header {
Some(raw_initial_header) => match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) {
Ok(initial_header) => Ok((
HeaderId(initial_header.number, initial_header.hash()),
raw_initial_header,
)),
Err(error) => Err(format!("Error decoding initial header: {}", error)),
},
Some(raw_initial_header) =>
match rialto_runtime::Header::decode(&mut &raw_initial_header[..]) {
Ok(initial_header) =>
Ok((HeaderId(initial_header.number, initial_header.hash()), raw_initial_header)),
Err(error) => Err(format!("Error decoding initial header: {}", error)),
},
None => {
let initial_header = sub_client.header_by_number(Zero::zero()).await;
initial_header
.map(|header| (HeaderId(Zero::zero(), header.hash()), header.encode()))
.map_err(|error| format!("Error reading Substrate genesis header: {:?}", error))
}
},
}
}
@@ -129,7 +132,8 @@ async fn prepare_initial_authorities_set(
None => sub_client.grandpa_authorities_set(sub_initial_header_hash).await,
};
initial_authorities_set.map_err(|error| format!("Error reading GRANDPA authorities set: {:?}", error))
initial_authorities_set
.map_err(|error| format!("Error reading GRANDPA authorities set: {:?}", error))
}
/// Deploy bridge contract to Ethereum chain.
@@ -147,7 +151,12 @@ async fn deploy_bridge_contract(
None,
None,
false,
bridge_contract::constructor(contract_code, initial_header, initial_set_id, initial_authorities),
bridge_contract::constructor(
contract_code,
initial_header,
initial_set_id,
initial_authorities,
),
)
.await
.map_err(|error| format!("Error deploying contract: {:?}", error))
@@ -16,28 +16,34 @@
//! Relaying proofs of PoA -> Substrate exchange transactions.
use crate::instances::BridgeInstance;
use crate::rialto_client::{SubmitEthereumExchangeTransactionProof, SubstrateHighLevelRpc};
use crate::rpc_errors::RpcError;
use crate::substrate_types::into_substrate_ethereum_receipt;
use crate::{
instances::BridgeInstance,
rialto_client::{SubmitEthereumExchangeTransactionProof, SubstrateHighLevelRpc},
rpc_errors::RpcError,
substrate_types::into_substrate_ethereum_receipt,
};
use async_trait::async_trait;
use bp_currency_exchange::MaybeLockFundsTransaction;
use exchange_relay::exchange::{
relay_single_transaction_proof, SourceBlock, SourceClient, SourceTransaction, TargetClient,
TransactionProofPipeline,
use exchange_relay::{
exchange::{
relay_single_transaction_proof, SourceBlock, SourceClient, SourceTransaction, TargetClient,
TransactionProofPipeline,
},
exchange_loop::{run as run_loop, InMemoryStorage},
};
use exchange_relay::exchange_loop::{run as run_loop, InMemoryStorage};
use relay_ethereum_client::{
types::{
HeaderId as EthereumHeaderId, HeaderWithTransactions as EthereumHeaderWithTransactions,
Transaction as EthereumTransaction, TransactionHash as EthereumTransactionHash, H256, HEADER_ID_PROOF,
Transaction as EthereumTransaction, TransactionHash as EthereumTransactionHash, H256,
HEADER_ID_PROOF,
},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{
Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams,
Chain as SubstrateChain, Client as SubstrateClient,
ConnectionParams as SubstrateConnectionParams,
};
use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient, HeaderId};
use rialto_runtime::exchange::EthereumTransactionInclusionProof;
@@ -111,12 +117,7 @@ impl SourceBlock for EthereumSourceBlock {
}
fn transactions(&self) -> Vec<Self::Transaction> {
self.0
.transactions
.iter()
.cloned()
.map(EthereumSourceTransaction)
.collect()
self.0.transactions.iter().cloned().map(EthereumSourceTransaction).collect()
}
}
@@ -178,13 +179,12 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
};
// we need transaction to be mined => check if it is included in the block
let (eth_header_id, eth_tx_index) = match (eth_tx.block_number, eth_tx.block_hash, eth_tx.transaction_index) {
(Some(block_number), Some(block_hash), Some(transaction_index)) => (
HeaderId(block_number.as_u64(), block_hash),
transaction_index.as_u64() as _,
),
_ => return Ok(None),
};
let (eth_header_id, eth_tx_index) =
match (eth_tx.block_number, eth_tx.block_hash, eth_tx.transaction_index) {
(Some(block_number), Some(block_hash), Some(transaction_index)) =>
(HeaderId(block_number.as_u64(), block_hash), transaction_index.as_u64() as _),
_ => return Ok(None),
};
Ok(Some((eth_header_id, eth_tx_index)))
}
@@ -194,9 +194,11 @@ impl SourceClient<EthereumToSubstrateExchange> for EthereumTransactionsSource {
block: &EthereumSourceBlock,
tx_index: usize,
) -> Result<EthereumTransactionInclusionProof, RpcError> {
const TRANSACTION_HAS_RAW_FIELD_PROOF: &str = "RPC level checks that transactions from Ethereum\
const TRANSACTION_HAS_RAW_FIELD_PROOF: &str =
"RPC level checks that transactions from Ethereum\
node are having `raw` field; qed";
const BLOCK_HAS_HASH_FIELD_PROOF: &str = "RPC level checks that block has `hash` field; qed";
const BLOCK_HAS_HASH_FIELD_PROOF: &str =
"RPC level checks that block has `hash` field; qed";
let mut transaction_proof = Vec::with_capacity(block.0.transactions.len());
for tx in &block.0.transactions {
@@ -266,12 +268,15 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
self.client.best_ethereum_finalized_block().await
}
async fn filter_transaction_proof(&self, proof: &EthereumTransactionInclusionProof) -> Result<bool, RpcError> {
async fn filter_transaction_proof(
&self,
proof: &EthereumTransactionInclusionProof,
) -> Result<bool, RpcError> {
// let's try to parse transaction locally
let (raw_tx, raw_tx_receipt) = &proof.proof[proof.index as usize];
let parse_result = rialto_runtime::exchange::EthTransaction::parse(raw_tx);
if parse_result.is_err() {
return Ok(false);
return Ok(false)
}
// now let's check if transaction is successful
@@ -285,8 +290,12 @@ impl TargetClient<EthereumToSubstrateExchange> for SubstrateTransactionsTarget {
self.client.verify_exchange_transaction_proof(proof.clone()).await
}
async fn submit_transaction_proof(&self, proof: EthereumTransactionInclusionProof) -> Result<(), RpcError> {
let (sign_params, bridge_instance) = (self.sign_params.clone(), self.bridge_instance.clone());
async fn submit_transaction_proof(
&self,
proof: EthereumTransactionInclusionProof,
) -> Result<(), RpcError> {
let (sign_params, bridge_instance) =
(self.sign_params.clone(), self.bridge_instance.clone());
self.client
.submit_exchange_transaction_proof(sign_params, bridge_instance, proof)
.await
@@ -311,9 +320,10 @@ pub async fn run(params: EthereumExchangeParams) {
err,
),
}
}
},
ExchangeRelayMode::Auto(eth_start_with_block_number) => {
let result = run_auto_transactions_relay_loop(params, eth_start_with_block_number).await;
let result =
run_auto_transactions_relay_loop(params, eth_start_with_block_number).await;
if let Err(err) = result {
log::error!(
target: "bridge",
@@ -321,23 +331,18 @@ pub async fn run(params: EthereumExchangeParams) {
err,
);
}
}
},
}
}
/// Run single transaction proof relay and stop.
async fn run_single_transaction_relay(params: EthereumExchangeParams, eth_tx_hash: H256) -> Result<(), String> {
let EthereumExchangeParams {
eth_params,
sub_params,
sub_sign,
instance,
..
} = params;
async fn run_single_transaction_relay(
params: EthereumExchangeParams,
eth_tx_hash: H256,
) -> Result<(), String> {
let EthereumExchangeParams { eth_params, sub_params, sub_sign, instance, .. } = params;
let eth_client = EthereumClient::try_connect(eth_params)
.await
.map_err(RpcError::Ethereum)?;
let eth_client = EthereumClient::try_connect(eth_params).await.map_err(RpcError::Ethereum)?;
let sub_client = SubstrateClient::<Rialto>::try_connect(sub_params)
.await
.map_err(RpcError::Substrate)?;
@@ -357,12 +362,7 @@ async fn run_auto_transactions_relay_loop(
eth_start_with_block_number: Option<u64>,
) -> anyhow::Result<()> {
let EthereumExchangeParams {
eth_params,
sub_params,
sub_sign,
metrics_params,
instance,
..
eth_params, sub_params, sub_sign, metrics_params, instance, ..
} = params;
let eth_client = EthereumClient::new(eth_params).await;
@@ -370,7 +370,7 @@ async fn run_auto_transactions_relay_loop(
let eth_start_with_block_number = match eth_start_with_block_number {
Some(eth_start_with_block_number) => eth_start_with_block_number,
None => {
None =>
sub_client
.best_ethereum_finalized_block()
.await
@@ -380,8 +380,7 @@ async fn run_auto_transactions_relay_loop(
err
)
})?
.0
}
.0,
};
run_loop(
@@ -22,7 +22,8 @@ use bp_eth_poa::{
};
use relay_ethereum_client::{
types::{CallRequest, U256},
Client as EthereumClient, ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
Client as EthereumClient, ConnectionParams as EthereumConnectionParams,
SigningParams as EthereumSigningParams,
};
use rialto_runtime::exchange::LOCK_FUNDS_ADDRESS;
@@ -43,13 +44,8 @@ pub struct EthereumExchangeSubmitParams {
/// Submit single Ethereum -> Substrate exchange transaction.
pub async fn run(params: EthereumExchangeSubmitParams) {
let EthereumExchangeSubmitParams {
eth_params,
eth_sign,
eth_nonce,
eth_amount,
sub_recipient,
} = params;
let EthereumExchangeSubmitParams { eth_params, eth_sign, eth_nonce, eth_amount, sub_recipient } =
params;
let result: Result<_, String> = async move {
let eth_client = EthereumClient::try_connect(eth_params)
@@ -83,9 +79,8 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
value: eth_amount,
payload: sub_recipient_encoded.to_vec(),
};
let eth_tx_signed = eth_tx_unsigned
.clone()
.sign_by(&eth_sign.signer, Some(eth_sign.chain_id));
let eth_tx_signed =
eth_tx_unsigned.clone().sign_by(&eth_sign.signer, Some(eth_sign.chain_id));
eth_client
.submit_transaction(eth_tx_signed)
.await
@@ -102,13 +97,13 @@ pub async fn run(params: EthereumExchangeSubmitParams) {
"Exchange transaction has been submitted to Ethereum node: {:?}",
eth_tx_unsigned,
);
}
},
Err(err) => {
log::error!(
target: "bridge",
"Error submitting exchange transaction to Ethereum node: {}",
err,
);
}
},
}
}
@@ -16,11 +16,13 @@
//! Ethereum PoA -> Rialto-Substrate synchronization.
use crate::ethereum_client::EthereumHighLevelRpc;
use crate::instances::BridgeInstance;
use crate::rialto_client::{SubmitEthereumHeaders, SubstrateHighLevelRpc};
use crate::rpc_errors::RpcError;
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
use crate::{
ethereum_client::EthereumHighLevelRpc,
instances::BridgeInstance,
rialto_client::{SubmitEthereumHeaders, SubstrateHighLevelRpc},
rpc_errors::RpcError,
substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts},
};
use async_trait::async_trait;
use codec::Encode;
@@ -35,12 +37,12 @@ use relay_ethereum_client::{
};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{
Chain as SubstrateChain, Client as SubstrateClient, ConnectionParams as SubstrateConnectionParams,
Chain as SubstrateChain, Client as SubstrateClient,
ConnectionParams as SubstrateConnectionParams,
};
use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient};
use std::fmt::Debug;
use std::{collections::HashSet, sync::Arc, time::Duration};
use std::{collections::HashSet, fmt::Debug, sync::Arc, time::Duration};
pub mod consts {
use super::*;
@@ -57,7 +59,8 @@ pub mod consts {
pub const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 128;
/// Max Ethereum headers count we want to have in 'submitted' state.
pub const MAX_SUBMITTED_HEADERS: usize = 128;
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten
/// (pruned).
pub const PRUNE_DEPTH: u32 = 4096;
}
@@ -106,8 +109,8 @@ impl HeadersSyncPipeline for EthereumHeadersSyncPipeline {
type Completion = ();
fn estimate_size(source: &QueuedHeader<Self>) -> usize {
into_substrate_ethereum_header(source.header()).encode().len()
+ into_substrate_ethereum_receipts(source.extra())
into_substrate_ethereum_header(source.header()).encode().len() +
into_substrate_ethereum_receipts(source.extra())
.map(|extra| extra.encode().len())
.unwrap_or(0)
}
@@ -148,22 +151,17 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
}
async fn header_by_hash(&self, hash: HeaderHash) -> Result<Header, RpcError> {
self.client
.header_by_hash(hash)
.await
.map(Into::into)
.map_err(Into::into)
self.client.header_by_hash(hash).await.map(Into::into).map_err(Into::into)
}
async fn header_by_number(&self, number: u64) -> Result<Header, RpcError> {
self.client
.header_by_number(number)
.await
.map(Into::into)
.map_err(Into::into)
self.client.header_by_number(number).await.map(Into::into).map_err(Into::into)
}
async fn header_completion(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, Option<()>), RpcError> {
async fn header_completion(
&self,
id: EthereumHeaderId,
) -> Result<(EthereumHeaderId, Option<()>), RpcError> {
Ok((id, None))
}
@@ -172,9 +170,7 @@ impl SourceClient<EthereumHeadersSyncPipeline> for EthereumHeadersSource {
id: EthereumHeaderId,
header: QueuedEthereumHeader,
) -> Result<(EthereumHeaderId, Vec<Receipt>), RpcError> {
self.client
.transaction_receipts(id, header.header().transactions.clone())
.await
self.client.transaction_receipts(id, header.header().transactions.clone()).await
}
}
@@ -197,12 +193,7 @@ impl SubstrateHeadersTarget {
sign_params: RialtoSigningParams,
bridge_instance: Arc<dyn BridgeInstance>,
) -> Self {
Self {
client,
sign_transactions,
sign_params,
bridge_instance,
}
Self { client, sign_transactions, sign_params, bridge_instance }
}
}
@@ -225,16 +216,19 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
self.client.best_ethereum_block().await
}
async fn is_known_header(&self, id: EthereumHeaderId) -> Result<(EthereumHeaderId, bool), RpcError> {
async fn is_known_header(
&self,
id: EthereumHeaderId,
) -> Result<(EthereumHeaderId, bool), RpcError> {
Ok((id, self.client.ethereum_header_known(id).await?))
}
async fn submit_headers(&self, headers: Vec<QueuedEthereumHeader>) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
let (sign_params, bridge_instance, sign_transactions) = (
self.sign_params.clone(),
self.bridge_instance.clone(),
self.sign_transactions,
);
async fn submit_headers(
&self,
headers: Vec<QueuedEthereumHeader>,
) -> SubmittedHeaders<EthereumHeaderId, RpcError> {
let (sign_params, bridge_instance, sign_transactions) =
(self.sign_params.clone(), self.bridge_instance.clone(), self.sign_transactions);
self.client
.submit_ethereum_headers(sign_params, bridge_instance, headers, sign_transactions)
.await
@@ -245,11 +239,18 @@ impl TargetClient<EthereumHeadersSyncPipeline> for SubstrateHeadersTarget {
}
#[allow(clippy::unit_arg)]
async fn complete_header(&self, id: EthereumHeaderId, _completion: ()) -> Result<EthereumHeaderId, RpcError> {
async fn complete_header(
&self,
id: EthereumHeaderId,
_completion: (),
) -> Result<EthereumHeaderId, RpcError> {
Ok(id)
}
async fn requires_extra(&self, header: QueuedEthereumHeader) -> Result<(EthereumHeaderId, bool), RpcError> {
async fn requires_extra(
&self,
header: QueuedEthereumHeader,
) -> Result<(EthereumHeaderId, bool), RpcError> {
// we can minimize number of receipts_check calls by checking header
// logs bloom here, but it may give us false positives (when authorities
// source is contract, we never need any logs)
+12 -8
View File
@@ -18,16 +18,18 @@
//! synchronizing a Substrate chain which can include multiple instances of the bridge pallet we
//! must somehow decide which of the instances to sync.
//!
//! Note that each instance of the bridge pallet is coupled with an instance of the currency exchange
//! pallet. We must also have a way to create `Call`s for the correct currency exchange instance.
//! Note that each instance of the bridge pallet is coupled with an instance of the currency
//! exchange pallet. We must also have a way to create `Call`s for the correct currency exchange
//! instance.
//!
//! This module helps by preparing the correct `Call`s for each of the different pallet instances.
use crate::ethereum_sync_loop::QueuedEthereumHeader;
use crate::substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts};
use crate::{
ethereum_sync_loop::QueuedEthereumHeader,
substrate_types::{into_substrate_ethereum_header, into_substrate_ethereum_receipts},
};
use rialto_runtime::exchange::EthereumTransactionInclusionProof as Proof;
use rialto_runtime::Call;
use rialto_runtime::{exchange::EthereumTransactionInclusionProof as Proof, Call};
/// Interface for `Calls` which are needed to correctly sync the bridge.
///
@@ -73,7 +75,8 @@ impl BridgeInstance for RialtoPoA {
}
fn build_currency_exchange_call(&self, proof: Proof) -> Call {
let pallet_call = rialto_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
let pallet_call =
rialto_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
rialto_runtime::Call::BridgeRialtoCurrencyExchange(pallet_call)
}
}
@@ -109,7 +112,8 @@ impl BridgeInstance for Kovan {
}
fn build_currency_exchange_call(&self, proof: Proof) -> Call {
let pallet_call = rialto_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
let pallet_call =
rialto_runtime::BridgeCurrencyExchangeCall::import_peer_transaction(proof);
rialto_runtime::Call::BridgeKovanCurrencyExchange(pallet_call)
}
}
+64 -55
View File
@@ -43,7 +43,9 @@ use sp_core::crypto::Pair;
use substrate_sync_loop::SubstrateSyncParams;
use headers_relay::sync::HeadersSyncParams;
use relay_ethereum_client::{ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams};
use relay_ethereum_client::{
ConnectionParams as EthereumConnectionParams, SigningParams as EthereumSigningParams,
};
use relay_rialto_client::SigningParams as RialtoSigningParams;
use relay_substrate_client::ConnectionParams as SubstrateConnectionParams;
use std::sync::Arc;
@@ -64,79 +66,83 @@ async fn run_command(matches: &clap::ArgMatches<'_>) {
Ok(ethereum_sync_params) => ethereum_sync_params,
Err(err) => {
log::error!(target: "bridge", "Error parsing parameters: {}", err);
return;
}
return
},
})
.await
.is_err()
{
log::error!(target: "bridge", "Unable to get Substrate genesis block for Ethereum sync.");
};
}
},
("sub-to-eth", Some(sub_to_eth_matches)) => {
log::info!(target: "bridge", "Starting SUB ➡ ETH relay.");
if substrate_sync_loop::run(match substrate_sync_params(sub_to_eth_matches) {
Ok(substrate_sync_params) => substrate_sync_params,
Err(err) => {
log::error!(target: "bridge", "Error parsing parameters: {}", err);
return;
}
return
},
})
.await
.is_err()
{
log::error!(target: "bridge", "Unable to get Substrate genesis block for Substrate sync.");
};
}
},
("eth-deploy-contract", Some(eth_deploy_matches)) => {
log::info!(target: "bridge", "Deploying ETH contracts.");
ethereum_deploy_contract::run(match ethereum_deploy_contract_params(eth_deploy_matches) {
Ok(ethereum_deploy_params) => ethereum_deploy_params,
Err(err) => {
log::error!(target: "bridge", "Error during contract deployment: {}", err);
return;
}
})
ethereum_deploy_contract::run(
match ethereum_deploy_contract_params(eth_deploy_matches) {
Ok(ethereum_deploy_params) => ethereum_deploy_params,
Err(err) => {
log::error!(target: "bridge", "Error during contract deployment: {}", err);
return
},
},
)
.await;
}
},
("eth-submit-exchange-tx", Some(eth_exchange_submit_matches)) => {
log::info!(target: "bridge", "Submitting ETH ➡ SUB exchange transaction.");
ethereum_exchange_submit::run(match ethereum_exchange_submit_params(eth_exchange_submit_matches) {
Ok(eth_exchange_submit_params) => eth_exchange_submit_params,
Err(err) => {
log::error!(target: "bridge", "Error submitting Eethereum exchange transaction: {}", err);
return;
}
})
ethereum_exchange_submit::run(
match ethereum_exchange_submit_params(eth_exchange_submit_matches) {
Ok(eth_exchange_submit_params) => eth_exchange_submit_params,
Err(err) => {
log::error!(target: "bridge", "Error submitting Eethereum exchange transaction: {}", err);
return
},
},
)
.await;
}
},
("eth-exchange-sub", Some(eth_exchange_matches)) => {
log::info!(target: "bridge", "Starting ETH ➡ SUB exchange transactions relay.");
ethereum_exchange::run(match ethereum_exchange_params(eth_exchange_matches) {
Ok(eth_exchange_params) => eth_exchange_params,
Err(err) => {
log::error!(target: "bridge", "Error relaying Ethereum transactions proofs: {}", err);
return;
}
return
},
})
.await;
}
},
("", _) => {
log::error!(target: "bridge", "No subcommand specified");
}
},
_ => unreachable!("all possible subcommands are checked above; qed"),
}
}
fn ethereum_connection_params(matches: &clap::ArgMatches) -> Result<EthereumConnectionParams, String> {
fn ethereum_connection_params(
matches: &clap::ArgMatches,
) -> Result<EthereumConnectionParams, String> {
let mut params = EthereumConnectionParams::default();
if let Some(eth_host) = matches.value_of("eth-host") {
params.host = eth_host.into();
}
if let Some(eth_port) = matches.value_of("eth-port") {
params.port = eth_port
.parse()
.map_err(|e| format!("Failed to parse eth-port: {}", e))?;
params.port = eth_port.parse().map_err(|e| format!("Failed to parse eth-port: {}", e))?;
}
Ok(params)
}
@@ -144,9 +150,10 @@ fn ethereum_connection_params(matches: &clap::ArgMatches) -> Result<EthereumConn
fn ethereum_signing_params(matches: &clap::ArgMatches) -> Result<EthereumSigningParams, String> {
let mut params = EthereumSigningParams::default();
if let Some(eth_signer) = matches.value_of("eth-signer") {
params.signer =
SecretKey::parse_slice(&hex::decode(eth_signer).map_err(|e| format!("Failed to parse eth-signer: {}", e))?)
.map_err(|e| format!("Invalid eth-signer: {}", e))?;
params.signer = SecretKey::parse_slice(
&hex::decode(eth_signer).map_err(|e| format!("Failed to parse eth-signer: {}", e))?,
)
.map_err(|e| format!("Invalid eth-signer: {}", e))?;
}
if let Some(eth_chain_id) = matches.value_of("eth-chain-id") {
params.chain_id = eth_chain_id
@@ -156,15 +163,15 @@ fn ethereum_signing_params(matches: &clap::ArgMatches) -> Result<EthereumSigning
Ok(params)
}
fn substrate_connection_params(matches: &clap::ArgMatches) -> Result<SubstrateConnectionParams, String> {
fn substrate_connection_params(
matches: &clap::ArgMatches,
) -> Result<SubstrateConnectionParams, String> {
let mut params = SubstrateConnectionParams::default();
if let Some(sub_host) = matches.value_of("sub-host") {
params.host = sub_host.into();
}
if let Some(sub_port) = matches.value_of("sub-port") {
params.port = sub_port
.parse()
.map_err(|e| format!("Failed to parse sub-port: {}", e))?;
params.port = sub_port.parse().map_err(|e| format!("Failed to parse sub-port: {}", e))?;
}
Ok(params)
}
@@ -199,7 +206,7 @@ fn ethereum_sync_params(matches: &clap::ArgMatches) -> Result<EthereumSyncParams
// tx pool won't accept too much unsigned transactions
sync_params.max_headers_in_submitted_status = 10;
}
},
Some("backup") => sync_params.target_tx_mode = TargetTransactionMode::Backup,
Some(mode) => return Err(format!("Invalid sub-tx-mode: {}", mode)),
None => sync_params.target_tx_mode = TargetTransactionMode::Signed,
@@ -252,10 +259,14 @@ fn substrate_sync_params(matches: &clap::ArgMatches) -> Result<SubstrateSyncPara
Ok(params)
}
fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<EthereumDeployContractParams, String> {
let eth_contract_code = parse_hex_argument(matches, "eth-contract-code")?.unwrap_or_else(|| {
hex::decode(include_str!("../res/substrate-bridge-bytecode.hex")).expect("code is hardcoded, thus valid; qed")
});
fn ethereum_deploy_contract_params(
matches: &clap::ArgMatches,
) -> Result<EthereumDeployContractParams, String> {
let eth_contract_code =
parse_hex_argument(matches, "eth-contract-code")?.unwrap_or_else(|| {
hex::decode(include_str!("../res/substrate-bridge-bytecode.hex"))
.expect("code is hardcoded, thus valid; qed")
});
let sub_initial_authorities_set_id = matches
.value_of("sub-authorities-set-id")
.map(|set| {
@@ -281,7 +292,9 @@ fn ethereum_deploy_contract_params(matches: &clap::ArgMatches) -> Result<Ethereu
Ok(params)
}
fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeSubmitParams, String> {
fn ethereum_exchange_submit_params(
matches: &clap::ArgMatches,
) -> Result<EthereumExchangeSubmitParams, String> {
let eth_nonce = matches
.value_of("eth-nonce")
.map(|eth_nonce| {
@@ -293,9 +306,7 @@ fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<Ethereu
let eth_amount = matches
.value_of("eth-amount")
.map(|eth_amount| {
eth_amount
.parse()
.map_err(|e| format!("Failed to parse eth-amount: {}", e))
eth_amount.parse().map_err(|e| format!("Failed to parse eth-amount: {}", e))
})
.transpose()?
.unwrap_or_else(|| {
@@ -304,7 +315,8 @@ fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<Ethereu
});
// This is the well-known Substrate account of Ferdie
let default_recepient = hex!("1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c");
let default_recepient =
hex!("1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c");
let sub_recipient = if let Some(sub_recipient) = matches.value_of("sub-recipient") {
hex::decode(&sub_recipient)
@@ -340,9 +352,7 @@ fn ethereum_exchange_submit_params(matches: &clap::ArgMatches) -> Result<Ethereu
fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchangeParams, String> {
let mode = match matches.value_of("eth-tx-hash") {
Some(eth_tx_hash) => ethereum_exchange::ExchangeRelayMode::Single(
eth_tx_hash
.parse()
.map_err(|e| format!("Failed to parse eth-tx-hash: {}", e))?,
eth_tx_hash.parse().map_err(|e| format!("Failed to parse eth-tx-hash: {}", e))?,
),
None => ethereum_exchange::ExchangeRelayMode::Auto(
matches
@@ -372,7 +382,7 @@ fn ethereum_exchange_params(matches: &clap::ArgMatches) -> Result<EthereumExchan
fn metrics_params(matches: &clap::ArgMatches) -> Result<MetricsParams, String> {
if matches.is_present("no-prometheus") {
return Ok(None.into());
return Ok(None.into())
}
let mut metrics_params = MetricsAddress::default();
@@ -405,9 +415,8 @@ fn instance_params(matches: &clap::ArgMatches) -> Result<Arc<dyn BridgeInstance>
fn parse_hex_argument(matches: &clap::ArgMatches, arg: &str) -> Result<Option<Vec<u8>>, String> {
match matches.value_of(arg) {
Some(value) => Ok(Some(
hex::decode(value).map_err(|e| format!("Failed to parse {}: {}", arg, e))?,
)),
Some(value) =>
Ok(Some(hex::decode(value).map_err(|e| format!("Failed to parse {}: {}", arg, e))?)),
None => Ok(None),
}
}
@@ -14,9 +14,9 @@
// 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::ethereum_sync_loop::QueuedEthereumHeader;
use crate::instances::BridgeInstance;
use crate::rpc_errors::RpcError;
use crate::{
ethereum_sync_loop::QueuedEthereumHeader, instances::BridgeInstance, rpc_errors::RpcError,
};
use async_trait::async_trait;
use bp_eth_poa::AuraHeader as SubstrateEthereumHeader;
@@ -24,7 +24,9 @@ use codec::{Decode, Encode};
use headers_relay::sync_types::SubmittedHeaders;
use relay_ethereum_client::types::HeaderId as EthereumHeaderId;
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{Client as SubstrateClient, TransactionSignScheme, UnsignedTransaction};
use relay_substrate_client::{
Client as SubstrateClient, TransactionSignScheme, UnsignedTransaction,
};
use relay_utils::HeaderId;
use sp_core::{crypto::Pair, Bytes};
use std::{collections::VecDeque, sync::Arc};
@@ -33,7 +35,8 @@ const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "RialtoPoAHeaderApi_is_import_req
const ETH_API_IS_KNOWN_BLOCK: &str = "RialtoPoAHeaderApi_is_known_block";
const ETH_API_BEST_BLOCK: &str = "RialtoPoAHeaderApi_best_block";
const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoPoAHeaderApi_finalized_block";
const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof";
const EXCH_API_FILTER_TRANSACTION_PROOF: &str =
"RialtoCurrencyExchangeApi_filter_transaction_proof";
type RpcResult<T> = std::result::Result<T, RpcError>;
@@ -58,7 +61,8 @@ impl SubstrateHighLevelRpc for SubstrateClient<Rialto> {
let data = Bytes(Vec::new());
let encoded_response = self.state_call(call, data, None).await?;
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
let decoded_response: (u64, bp_eth_poa::H256) =
Decode::decode(&mut &encoded_response.0[..])?;
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
Ok(best_header_id)
@@ -69,7 +73,8 @@ impl SubstrateHighLevelRpc for SubstrateClient<Rialto> {
let data = Bytes(Vec::new());
let encoded_response = self.state_call(call, data, None).await?;
let decoded_response: (u64, bp_eth_poa::H256) = Decode::decode(&mut &encoded_response.0[..])?;
let decoded_response: (u64, bp_eth_poa::H256) =
Decode::decode(&mut &encoded_response.0[..])?;
let best_header_id = HeaderId(decoded_response.0, decoded_response.1);
Ok(best_header_id)
@@ -157,17 +162,23 @@ impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
let ids = headers.iter().map(|header| header.id()).collect();
let genesis_hash = *self.genesis_hash();
let submission_result = async {
self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |_, transaction_nonce| {
Bytes(
Rialto::sign_transaction(
genesis_hash,
&params,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(instance.build_signed_header_call(headers), transaction_nonce),
self.submit_signed_extrinsic(
(*params.public().as_array_ref()).into(),
move |_, transaction_nonce| {
Bytes(
Rialto::sign_transaction(
genesis_hash,
&params,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
instance.build_signed_header_call(headers),
transaction_nonce,
),
)
.encode(),
)
.encode(),
)
})
},
)
.await?;
Ok(())
}
@@ -209,8 +220,8 @@ impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
submitted_headers.rejected.push(id);
submitted_headers.rejected.extend(ids);
submitted_headers.fatal_error = Some(error.into());
break;
}
break
},
}
}
@@ -259,23 +270,31 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateClient<Rialto> {
proof: rialto_runtime::exchange::EthereumTransactionInclusionProof,
) -> RpcResult<()> {
let genesis_hash = *self.genesis_hash();
self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |_, transaction_nonce| {
Bytes(
Rialto::sign_transaction(
genesis_hash,
&params,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(instance.build_currency_exchange_call(proof), transaction_nonce),
self.submit_signed_extrinsic(
(*params.public().as_array_ref()).into(),
move |_, transaction_nonce| {
Bytes(
Rialto::sign_transaction(
genesis_hash,
&params,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
instance.build_currency_exchange_call(proof),
transaction_nonce,
),
)
.encode(),
)
.encode(),
)
})
},
)
.await?;
Ok(())
}
}
/// Create unsigned Substrate transaction for submitting Ethereum header.
fn create_unsigned_submit_transaction(call: rialto_runtime::Call) -> rialto_runtime::UncheckedExtrinsic {
fn create_unsigned_submit_transaction(
call: rialto_runtime::Call,
) -> rialto_runtime::UncheckedExtrinsic {
rialto_runtime::UncheckedExtrinsic::new_unsigned(call)
}
@@ -16,8 +16,7 @@
//! Rialto-Substrate -> Ethereum PoA synchronization.
use crate::ethereum_client::EthereumHighLevelRpc;
use crate::rpc_errors::RpcError;
use crate::{ethereum_client::EthereumHighLevelRpc, rpc_errors::RpcError};
use async_trait::async_trait;
use codec::Encode;
@@ -38,8 +37,7 @@ use relay_substrate_client::{
use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient};
use sp_runtime::EncodedJustification;
use std::fmt::Debug;
use std::{collections::HashSet, time::Duration};
use std::{collections::HashSet, fmt::Debug, time::Duration};
pub mod consts {
use super::*;
@@ -50,7 +48,8 @@ pub mod consts {
pub const MAX_FUTURE_HEADERS_TO_DOWNLOAD: usize = 8;
/// Max Ethereum headers count we want to have in 'submitted' state.
pub const MAX_SUBMITTED_HEADERS: usize = 4;
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten (pruned).
/// Max depth of in-memory headers in all states. Past this depth they will be forgotten
/// (pruned).
pub const PRUNE_DEPTH: u32 = 256;
}
@@ -110,11 +109,7 @@ struct EthereumHeadersTarget {
impl EthereumHeadersTarget {
fn new(client: EthereumClient, contract: Address, sign_params: EthereumSigningParams) -> Self {
Self {
client,
contract,
sign_params,
}
Self { client, contract, sign_params }
}
}
@@ -137,11 +132,17 @@ impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
self.client.best_substrate_block(self.contract).await
}
async fn is_known_header(&self, id: RialtoHeaderId) -> Result<(RialtoHeaderId, bool), RpcError> {
async fn is_known_header(
&self,
id: RialtoHeaderId,
) -> Result<(RialtoHeaderId, bool), RpcError> {
self.client.substrate_header_known(self.contract, id).await
}
async fn submit_headers(&self, headers: Vec<QueuedRialtoHeader>) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
async fn submit_headers(
&self,
headers: Vec<QueuedRialtoHeader>,
) -> SubmittedHeaders<RialtoHeaderId, RpcError> {
self.client
.submit_substrate_headers(self.sign_params.clone(), self.contract, headers)
.await
@@ -161,7 +162,10 @@ impl TargetClient<SubstrateHeadersSyncPipeline> for EthereumHeadersTarget {
.await
}
async fn requires_extra(&self, header: QueuedRialtoHeader) -> Result<(RialtoHeaderId, bool), RpcError> {
async fn requires_extra(
&self,
header: QueuedRialtoHeader,
) -> Result<(RialtoHeaderId, bool), RpcError> {
Ok((header.header().id(), false))
}
}
@@ -17,11 +17,12 @@
//! Converting between Ethereum headers and bridge module types.
use bp_eth_poa::{
AuraHeader as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry, Receipt as SubstrateEthereumReceipt,
TransactionOutcome as SubstrateEthereumTransactionOutcome,
AuraHeader as SubstrateEthereumHeader, LogEntry as SubstrateEthereumLogEntry,
Receipt as SubstrateEthereumReceipt, TransactionOutcome as SubstrateEthereumTransactionOutcome,
};
use relay_ethereum_client::types::{
Header as EthereumHeader, Receipt as EthereumReceipt, HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
Header as EthereumHeader, Receipt as EthereumReceipt,
HEADER_ID_PROOF as ETHEREUM_HEADER_ID_PROOF,
};
/// Convert Ethereum header into Ethereum header for Substrate.
@@ -68,7 +69,8 @@ pub fn into_substrate_ethereum_receipt(receipt: &EthereumReceipt) -> SubstrateEt
})
.collect(),
outcome: match (receipt.status, receipt.root) {
(Some(status), None) => SubstrateEthereumTransactionOutcome::StatusCode(status.as_u64() as u8),
(Some(status), None) =>
SubstrateEthereumTransactionOutcome::StatusCode(status.as_u64() as u8),
(None, Some(root)) => SubstrateEthereumTransactionOutcome::StateRoot(root),
_ => SubstrateEthereumTransactionOutcome::Unknown,
},
@@ -41,41 +41,41 @@ impl CliEncodeCall for Kusama {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_kusama_client::runtime::Call::System(relay_kusama_client::runtime::SystemCall::remark(
Call::Remark { remark_payload, .. } => relay_kusama_client::runtime::Call::System(
relay_kusama_client::runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
))
}
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::KUSAMA_TO_POLKADOT_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
),
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::KUSAMA_TO_POLKADOT_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(
lane.0, payload, fee.0,
),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
})
}
fn get_dispatch_info(call: &relay_kusama_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
fn get_dispatch_info(
call: &relay_kusama_client::runtime::Call,
) -> anyhow::Result<DispatchInfo> {
match *call {
relay_kusama_client::runtime::Call::System(relay_kusama_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
relay_kusama_client::runtime::Call::System(
relay_kusama_client::runtime::SystemCall::remark(_),
) => Ok(DispatchInfo {
weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
}),
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
}
}
@@ -95,7 +95,9 @@ impl CliChain for Kusama {
bp_kusama::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
_message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
Err("Sending messages from Kusama is not yet supported.".into())
}
}
@@ -24,13 +24,15 @@ use relay_kusama_client::{Kusama, SyncHeader as KusamaSyncHeader};
use relay_polkadot_client::{Polkadot, SigningParams as PolkadotSigningParams};
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
///
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 21 DOT,
/// but let's round up to 30 DOT here.
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 21
/// DOT, but let's round up to 30 DOT here.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 30_000_000_000;
/// Kusama-to-Polkadot finality sync pipeline.
@@ -45,7 +47,10 @@ pub(crate) struct KusamaFinalityToPolkadot {
impl KusamaFinalityToPolkadot {
pub fn new(target_client: Client<Polkadot>, target_sign: PolkadotSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot::new(target_client, target_sign),
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot::new(
target_client,
target_sign,
),
}
}
}
@@ -53,7 +58,8 @@ impl KusamaFinalityToPolkadot {
impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
type FinalitySyncPipeline = FinalityPipelineKusamaFinalityToPolkadot;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
type TargetChain = Polkadot;
@@ -116,29 +122,36 @@ pub(crate) mod tests {
B: From<u32> + std::ops::Mul<Output = B>,
W: WeightToFeePolynomial<Balance = B>,
{
// we assume that the GRANDPA is not lagging here => ancestry length will be near to 0 (let's round up to 2)
// we assume that the GRANDPA is not lagging here => ancestry length will be near to 0
// (let's round up to 2)
const AVG_VOTES_ANCESTRIES_LEN: u32 = 2;
// let's assume number of validators is 1024 (more than on any existing well-known chain atm)
// => number of precommits is *2/3 + 1
// let's assume number of validators is 1024 (more than on any existing well-known chain
// atm) => number of precommits is *2/3 + 1
const AVG_PRECOMMITS_LEN: u32 = 1024 * 2 / 3 + 1;
// GRANDPA pallet weights. We're now using Rialto weights everywhere.
//
// Using Rialto runtime is slightly incorrect, because `DbWeight` of other runtimes may differ
// from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is the same.
type GrandpaPalletWeights = pallet_bridge_grandpa::weights::RialtoWeight<rialto_runtime::Runtime>;
// Using Rialto runtime is slightly incorrect, because `DbWeight` of other runtimes may
// differ from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is
// the same.
type GrandpaPalletWeights =
pallet_bridge_grandpa::weights::RialtoWeight<rialto_runtime::Runtime>;
// The following formula shall not be treated as super-accurate - guard is to protect from mad relays,
// not to protect from over-average loses.
// The following formula shall not be treated as super-accurate - guard is to protect from
// mad relays, not to protect from over-average loses.
// increase number of headers a bit
let expected_source_headers_per_day = expected_source_headers_per_day * 110 / 100;
let single_source_header_submit_call_weight =
GrandpaPalletWeights::submit_finality_proof(AVG_VOTES_ANCESTRIES_LEN, AVG_PRECOMMITS_LEN);
// for simplicity - add extra weight for base tx fee + fee that is paid for the tx size + adjusted fee
let single_source_header_submit_call_weight = GrandpaPalletWeights::submit_finality_proof(
AVG_VOTES_ANCESTRIES_LEN,
AVG_PRECOMMITS_LEN,
);
// for simplicity - add extra weight for base tx fee + fee that is paid for the tx size +
// adjusted fee
let single_source_header_submit_tx_weight = single_source_header_submit_call_weight * 3 / 2;
let single_source_header_tx_cost = W::calc(&single_source_header_submit_tx_weight);
let maximal_expected_decrease = single_source_header_tx_cost * B::from(expected_source_headers_per_day);
let maximal_expected_decrease =
single_source_header_tx_cost * B::from(expected_source_headers_per_day);
maximal_expected_decrease
}
@@ -25,17 +25,23 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_kusama_client::{HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams};
use relay_polkadot_client::{HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams};
use relay_kusama_client::{
HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams,
};
use relay_polkadot_client::{
HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams,
};
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use sp_runtime::{FixedPointNumber, FixedU128};
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Kusama-to-Polkadot message lane.
pub type MessageLaneKusamaMessagesToPolkadot =
@@ -49,24 +55,32 @@ pub struct KusamaMessagesToPolkadot {
impl SubstrateMessageLane for KusamaMessagesToPolkadot {
type MessageLane = MessageLaneKusamaMessagesToPolkadot;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_polkadot::TO_POLKADOT_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_polkadot::TO_POLKADOT_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_kusama::FROM_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_kusama::FROM_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_kusama::FROM_KUSAMA_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_kusama::FROM_KUSAMA_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_kusama::FROM_KUSAMA_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str =
bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str =
bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Kusama;
type TargetChain = Polkadot;
@@ -117,11 +131,7 @@ impl SubstrateMessageLane for KusamaMessagesToPolkadot {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call = relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
@@ -180,14 +190,14 @@ pub async fn run(
// we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using
// weights from Rialto and then simply dividing it by x2.
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>,
>(
bp_polkadot::max_extrinsic_weight(),
bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
max_messages_in_single_batch / 2,
max_messages_weight_in_single_batch / 2,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
log::info!(
target: "bridge",
@@ -219,8 +229,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_polkadot::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_polkadot::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -252,8 +264,10 @@ pub(crate) fn add_standalone_metrics(
metrics_params: MetricsParams,
source_client: Client<Kusama>,
) -> anyhow::Result<(MetricsParams, StandaloneMessagesMetrics)> {
let polkadot_to_kusama_conversion_rate_key =
bp_runtime::storage_parameter_key(bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME).0;
let polkadot_to_kusama_conversion_rate_key = bp_runtime::storage_parameter_key(
bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME,
)
.0;
substrate_relay_helper::messages_lane::add_standalone_metrics::<KusamaMessagesToPolkadot>(
metrics_prefix,
@@ -37,31 +37,26 @@ impl CliEncodeCall for Millau {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Raw { data } => Decode::decode(&mut &*data.0)?,
Call::Remark { remark_payload, .. } => millau_runtime::Call::System(millau_runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
)),
Call::Remark { remark_payload, .. } =>
millau_runtime::Call::System(millau_runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
)),
Call::Transfer { recipient, amount } => millau_runtime::Call::Balances(
millau_runtime::BalancesCall::transfer(recipient.raw_id(), amount.cast()),
),
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::MILLAU_TO_RIALTO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
millau_runtime::Call::BridgeRialtoMessages(millau_runtime::MessagesCall::send_message(
lane.0,
payload,
fee.cast(),
))
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::MILLAU_TO_RIALTO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
millau_runtime::Call::BridgeRialtoMessages(
millau_runtime::MessagesCall::send_message(lane.0, payload, fee.cast()),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
})
}
@@ -74,7 +69,12 @@ impl CliChain for Millau {
const RUNTIME_VERSION: RuntimeVersion = millau_runtime::VERSION;
type KeyPair = sp_core::sr25519::Pair;
type MessagePayload = MessagePayload<bp_millau::AccountId, bp_rialto::AccountSigner, bp_rialto::Signature, Vec<u8>>;
type MessagePayload = MessagePayload<
bp_millau::AccountId,
bp_rialto::AccountSigner,
bp_rialto::Signature,
Vec<u8>,
>;
fn ss58_format() -> u16 {
millau_runtime::SS58Prefix::get() as u16
@@ -85,7 +85,9 @@ impl CliChain for Millau {
}
// TODO [#854|#843] support multiple bridges?
fn encode_message(message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
match message {
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
.map_err(|e| format!("Failed to decode Millau's MessagePayload: {:?}", e)),
@@ -96,7 +98,10 @@ impl CliChain for Millau {
sender.enforce_chain::<Source>();
let spec_version = Target::RUNTIME_VERSION.spec_version;
let origin = CallOrigin::SourceAccount(sender.raw_id());
encode_call::preprocess_call::<Source, Target>(&mut call, bridge::MILLAU_TO_RIALTO_INDEX);
encode_call::preprocess_call::<Source, Target>(
&mut call,
bridge::MILLAU_TO_RIALTO_INDEX,
);
let call = Target::encode_call(&call).map_err(|e| e.to_string())?;
let weight = call.get_dispatch_info().weight;
@@ -107,7 +112,7 @@ impl CliChain for Millau {
&call,
DispatchFeePayment::AtSourceChain,
))
}
},
}
}
}
@@ -23,10 +23,13 @@ use bp_header_chain::justification::GrandpaJustification;
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Millau-to-Rialto finality sync pipeline.
pub(crate) type FinalityPipelineMillauToRialto = SubstrateFinalityToSubstrate<Millau, Rialto, RialtoSigningParams>;
pub(crate) type FinalityPipelineMillauToRialto =
SubstrateFinalityToSubstrate<Millau, Rialto, RialtoSigningParams>;
#[derive(Clone, Debug)]
pub(crate) struct MillauFinalityToRialto {
@@ -35,16 +38,15 @@ pub(crate) struct MillauFinalityToRialto {
impl MillauFinalityToRialto {
pub fn new(target_client: Client<Rialto>, target_sign: RialtoSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineMillauToRialto::new(target_client, target_sign),
}
Self { finality_pipeline: FinalityPipelineMillauToRialto::new(target_client, target_sign) }
}
}
impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
type FinalitySyncPipeline = FinalityPipelineMillauToRialto;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
type TargetChain = Rialto;
@@ -59,8 +61,11 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
header: MillauSyncHeader,
proof: GrandpaJustification<bp_millau::Header>,
) -> Bytes {
let call =
rialto_runtime::BridgeGrandpaMillauCall::submit_finality_proof(Box::new(header.into_inner()), proof).into();
let call = rialto_runtime::BridgeGrandpaMillauCall::submit_finality_proof(
Box::new(header.into_inner()),
proof,
)
.into();
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
let transaction = Rialto::sign_transaction(
@@ -26,16 +26,22 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
use relay_millau_client::{
HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams,
};
use relay_rialto_client::{
HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams,
};
use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Millau-to-Rialto message lane.
pub type MessageLaneMillauMessagesToRialto =
@@ -49,23 +55,30 @@ pub struct MillauMessagesToRialto {
impl SubstrateMessageLane for MillauMessagesToRialto {
type MessageLane = MessageLaneMillauMessagesToRialto;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_rialto::TO_RIALTO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rialto::TO_RIALTO_LATEST_RECEIVED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_rialto::TO_RIALTO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_millau::FROM_MILLAU_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_millau::FROM_MILLAU_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_millau::FROM_MILLAU_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_millau::FROM_MILLAU_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_millau::FROM_MILLAU_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Millau;
type TargetChain = Rialto;
@@ -82,7 +95,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
) -> Bytes {
let (relayers_state, proof) = proof;
let call: millau_runtime::Call =
millau_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state).into();
millau_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state)
.into();
let call_weight = call.get_dispatch_info().weight;
let genesis_hash = *self.message_lane.source_client.genesis_hash();
let transaction = Millau::sign_transaction(
@@ -114,11 +128,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call: rialto_runtime::Call = rialto_runtime::MessagesCall::receive_messages_proof(
self.message_lane.relayer_id_at_source.clone(),
@@ -176,7 +186,9 @@ pub async fn run(
let max_messages_size_in_single_batch = bp_rialto::max_extrinsic_size() / 3;
// TODO: use Millau weights after https://github.com/paritytech/parity-bridges-common/issues/390
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<millau_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<millau_runtime::Runtime>,
>(
bp_rialto::max_extrinsic_weight(),
bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
@@ -211,8 +223,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -251,7 +265,9 @@ pub(crate) fn add_standalone_metrics(
Some(crate::chains::MILLAU_ASSOCIATED_TOKEN_ID),
Some(crate::chains::RIALTO_ASSOCIATED_TOKEN_ID),
Some((
sp_core::storage::StorageKey(millau_runtime::rialto_messages::RialtoToMillauConversionRate::key().to_vec()),
sp_core::storage::StorageKey(
millau_runtime::rialto_messages::RialtoToMillauConversionRate::key().to_vec(),
),
millau_runtime::rialto_messages::INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE,
)),
)
+21 -13
View File
@@ -38,9 +38,9 @@ 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).
// 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;
@@ -53,8 +53,8 @@ pub(crate) fn add_polkadot_kusama_price_metrics<T: finality_relay::FinalitySyncP
prefix: Option<String>,
params: MetricsParams,
) -> anyhow::Result<MetricsParams> {
// 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
// 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")
@@ -92,7 +92,8 @@ mod tests {
rialto_runtime::VERSION.spec_version,
);
let rialto_signer = relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap();
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()));
@@ -113,7 +114,8 @@ mod tests {
millau_runtime::VERSION.spec_version,
);
let millau_signer = relay_millau_client::SigningParams::from_string("//Dave", None).unwrap();
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()));
@@ -128,7 +130,8 @@ mod tests {
bp_millau::max_extrinsic_size(),
);
let call: millau_runtime::Call = millau_runtime::SystemCall::remark(vec![42; maximal_remark_size as _]).into();
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,
@@ -164,8 +167,9 @@ mod tests {
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 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(
@@ -191,8 +195,9 @@ mod tests {
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 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(
@@ -325,7 +330,10 @@ mod westend_tests {
votes_ancestries: vec![],
};
let actual = bp_westend::BridgeGrandpaRococoCall::submit_finality_proof(header.clone(), justification.clone());
let actual = bp_westend::BridgeGrandpaRococoCall::submit_finality_proof(
header.clone(),
justification.clone(),
);
let expected = millau_runtime::BridgeGrandpaRialtoCall::<millau_runtime::Runtime>::submit_finality_proof(
Box::new(header),
justification,
@@ -41,41 +41,41 @@ impl CliEncodeCall for Polkadot {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_polkadot_client::runtime::Call::System(relay_polkadot_client::runtime::SystemCall::remark(
Call::Remark { remark_payload, .. } => relay_polkadot_client::runtime::Call::System(
relay_polkadot_client::runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
))
}
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::POLKADOT_TO_KUSAMA_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
),
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::POLKADOT_TO_KUSAMA_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(
lane.0, payload, fee.0,
),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
})
}
fn get_dispatch_info(call: &relay_polkadot_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
fn get_dispatch_info(
call: &relay_polkadot_client::runtime::Call,
) -> anyhow::Result<DispatchInfo> {
match *call {
relay_polkadot_client::runtime::Call::System(relay_polkadot_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
relay_polkadot_client::runtime::Call::System(
relay_polkadot_client::runtime::SystemCall::remark(_),
) => Ok(DispatchInfo {
weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
}),
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
}
}
@@ -95,7 +95,9 @@ impl CliChain for Polkadot {
bp_polkadot::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
_message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
Err("Sending messages from Polkadot is not yet supported.".into())
}
}
@@ -24,13 +24,15 @@ use relay_kusama_client::{Kusama, SigningParams as KusamaSigningParams};
use relay_polkadot_client::{Polkadot, SyncHeader as PolkadotSyncHeader};
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
///
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 0.001 KSM,
/// but let's round up to 0.1 KSM here.
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 0.001
/// KSM, but let's round up to 0.1 KSM here.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 100_000_000_000;
/// Polkadot-to-Kusama finality sync pipeline.
@@ -45,7 +47,10 @@ pub(crate) struct PolkadotFinalityToKusama {
impl PolkadotFinalityToKusama {
pub fn new(target_client: Client<Kusama>, target_sign: KusamaSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama::new(target_client, target_sign),
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama::new(
target_client,
target_sign,
),
}
}
}
@@ -53,7 +58,8 @@ impl PolkadotFinalityToKusama {
impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
type FinalitySyncPipeline = FinalityPipelinePolkadotFinalityToKusama;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
type TargetChain = Kusama;
@@ -25,17 +25,23 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_kusama_client::{HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams};
use relay_polkadot_client::{HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams};
use relay_kusama_client::{
HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams,
};
use relay_polkadot_client::{
HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams,
};
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use sp_runtime::{FixedPointNumber, FixedU128};
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Polkadot-to-Kusama message lane.
pub type MessageLanePolkadotMessagesToKusama =
@@ -48,24 +54,32 @@ pub struct PolkadotMessagesToKusama {
impl SubstrateMessageLane for PolkadotMessagesToKusama {
type MessageLane = MessageLanePolkadotMessagesToKusama;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_kusama::TO_KUSAMA_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_kusama::TO_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_kusama::TO_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_polkadot::FROM_POLKADOT_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_polkadot::FROM_POLKADOT_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_polkadot::FROM_POLKADOT_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_polkadot::FROM_POLKADOT_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str =
bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str =
bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Polkadot;
type TargetChain = Kusama;
@@ -116,11 +130,7 @@ impl SubstrateMessageLane for PolkadotMessagesToKusama {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call = relay_kusama_client::runtime::Call::BridgePolkadotMessages(
@@ -179,14 +189,14 @@ pub async fn run(
// we don't know exact weights of the Kusama runtime. So to guess weights we'll be using
// weights from Rialto and then simply dividing it by x2.
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>,
>(
bp_kusama::max_extrinsic_weight(),
bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
max_messages_in_single_batch / 2,
max_messages_weight_in_single_batch / 2,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
log::info!(
target: "bridge",
@@ -218,8 +228,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_kusama::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_kusama::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -251,8 +263,10 @@ pub(crate) fn add_standalone_metrics(
metrics_params: MetricsParams,
source_client: Client<Polkadot>,
) -> anyhow::Result<(MetricsParams, StandaloneMessagesMetrics)> {
let kusama_to_polkadot_conversion_rate_key =
bp_runtime::storage_parameter_key(bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME).0;
let kusama_to_polkadot_conversion_rate_key = bp_runtime::storage_parameter_key(
bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME,
)
.0;
substrate_relay_helper::messages_lane::add_standalone_metrics::<PolkadotMessagesToKusama>(
metrics_prefix,
@@ -37,29 +37,26 @@ impl CliEncodeCall for Rialto {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Raw { data } => Decode::decode(&mut &*data.0)?,
Call::Remark { remark_payload, .. } => rialto_runtime::Call::System(rialto_runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
)),
Call::Remark { remark_payload, .. } =>
rialto_runtime::Call::System(rialto_runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
)),
Call::Transfer { recipient, amount } => rialto_runtime::Call::Balances(
rialto_runtime::BalancesCall::transfer(recipient.raw_id().into(), amount.0),
),
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::RIALTO_TO_MILLAU_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
rialto_runtime::Call::BridgeMillauMessages(rialto_runtime::MessagesCall::send_message(
lane.0, payload, fee.0,
))
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::RIALTO_TO_MILLAU_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
rialto_runtime::Call::BridgeMillauMessages(
rialto_runtime::MessagesCall::send_message(lane.0, payload, fee.0),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
})
}
@@ -72,7 +69,12 @@ impl CliChain for Rialto {
const RUNTIME_VERSION: RuntimeVersion = rialto_runtime::VERSION;
type KeyPair = sp_core::sr25519::Pair;
type MessagePayload = MessagePayload<bp_rialto::AccountId, bp_millau::AccountSigner, bp_millau::Signature, Vec<u8>>;
type MessagePayload = MessagePayload<
bp_rialto::AccountId,
bp_millau::AccountSigner,
bp_millau::Signature,
Vec<u8>,
>;
fn ss58_format() -> u16 {
rialto_runtime::SS58Prefix::get() as u16
@@ -82,7 +84,9 @@ impl CliChain for Rialto {
bp_rialto::max_extrinsic_weight()
}
fn encode_message(message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
match message {
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
.map_err(|e| format!("Failed to decode Rialto's MessagePayload: {:?}", e)),
@@ -93,7 +97,10 @@ impl CliChain for Rialto {
sender.enforce_chain::<Source>();
let spec_version = Target::RUNTIME_VERSION.spec_version;
let origin = CallOrigin::SourceAccount(sender.raw_id());
encode_call::preprocess_call::<Source, Target>(&mut call, bridge::RIALTO_TO_MILLAU_INDEX);
encode_call::preprocess_call::<Source, Target>(
&mut call,
bridge::RIALTO_TO_MILLAU_INDEX,
);
let call = Target::encode_call(&call).map_err(|e| e.to_string())?;
let weight = call.get_dispatch_info().weight;
@@ -104,7 +111,7 @@ impl CliChain for Rialto {
&call,
DispatchFeePayment::AtSourceChain,
))
}
},
}
}
}
@@ -23,7 +23,9 @@ use bp_header_chain::justification::GrandpaJustification;
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Rialto-to-Millau finality sync pipeline.
pub(crate) type FinalityPipelineRialtoFinalityToMillau =
@@ -37,7 +39,10 @@ pub struct RialtoFinalityToMillau {
impl RialtoFinalityToMillau {
pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineRialtoFinalityToMillau::new(target_client, target_sign),
finality_pipeline: FinalityPipelineRialtoFinalityToMillau::new(
target_client,
target_sign,
),
}
}
}
@@ -45,7 +50,8 @@ impl RialtoFinalityToMillau {
impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
type FinalitySyncPipeline = FinalityPipelineRialtoFinalityToMillau;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
type TargetChain = Millau;
@@ -26,16 +26,22 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
use relay_millau_client::{
HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams,
};
use relay_rialto_client::{
HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams,
};
use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Rialto-to-Millau message lane.
pub type MessageLaneRialtoMessagesToMillau =
@@ -49,23 +55,30 @@ pub struct RialtoMessagesToMillau {
impl SubstrateMessageLane for RialtoMessagesToMillau {
type MessageLane = MessageLaneRialtoMessagesToMillau;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_millau::TO_MILLAU_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_millau::TO_MILLAU_LATEST_RECEIVED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_millau::TO_MILLAU_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rialto::FROM_RIALTO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_rialto::FROM_RIALTO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_rialto::FROM_RIALTO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_rialto::FROM_RIALTO_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_rialto::FROM_RIALTO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Rialto;
type TargetChain = Millau;
@@ -82,7 +95,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
) -> Bytes {
let (relayers_state, proof) = proof;
let call: rialto_runtime::Call =
rialto_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state).into();
rialto_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state)
.into();
let call_weight = call.get_dispatch_info().weight;
let genesis_hash = *self.message_lane.source_client.genesis_hash();
let transaction = Rialto::sign_transaction(
@@ -114,11 +128,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call: millau_runtime::Call = millau_runtime::MessagesCall::receive_messages_proof(
self.message_lane.relayer_id_at_source.clone(),
@@ -175,7 +185,9 @@ pub async fn run(
// 2/3 is reserved for proofs and tx overhead
let max_messages_size_in_single_batch = bp_millau::max_extrinsic_size() / 3;
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>,
>(
bp_millau::max_extrinsic_weight(),
bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
@@ -210,8 +222,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -250,7 +264,9 @@ pub(crate) fn add_standalone_metrics(
Some(crate::chains::RIALTO_ASSOCIATED_TOKEN_ID),
Some(crate::chains::MILLAU_ASSOCIATED_TOKEN_ID),
Some((
sp_core::storage::StorageKey(rialto_runtime::millau_messages::MillauToRialtoConversionRate::key().to_vec()),
sp_core::storage::StorageKey(
rialto_runtime::millau_messages::MillauToRialtoConversionRate::key().to_vec(),
),
rialto_runtime::millau_messages::INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE,
)),
)
@@ -38,41 +38,41 @@ impl CliEncodeCall for Rococo {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_rococo_client::runtime::Call::System(relay_rococo_client::runtime::SystemCall::remark(
Call::Remark { remark_payload, .. } => relay_rococo_client::runtime::Call::System(
relay_rococo_client::runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
))
}
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::ROCOCO_TO_WOCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_rococo_client::runtime::Call::BridgeMessagesWococo(
relay_rococo_client::runtime::BridgeMessagesWococoCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
),
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::ROCOCO_TO_WOCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_rococo_client::runtime::Call::BridgeMessagesWococo(
relay_rococo_client::runtime::BridgeMessagesWococoCall::send_message(
lane.0, payload, fee.0,
),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("The call is not supported"),
})
}
fn get_dispatch_info(call: &relay_rococo_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
fn get_dispatch_info(
call: &relay_rococo_client::runtime::Call,
) -> anyhow::Result<DispatchInfo> {
match *call {
relay_rococo_client::runtime::Call::System(relay_rococo_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
relay_rococo_client::runtime::Call::System(
relay_rococo_client::runtime::SystemCall::remark(_),
) => Ok(DispatchInfo {
weight: SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
}),
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
}
}
@@ -92,7 +92,9 @@ impl CliChain for Rococo {
bp_wococo::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
_message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
Err("Sending messages from Rococo is not yet supported.".into())
}
}
@@ -24,7 +24,9 @@ use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo};
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY;
@@ -40,7 +42,10 @@ pub(crate) struct RococoFinalityToWococo {
impl RococoFinalityToWococo {
pub fn new(target_client: Client<Wococo>, target_sign: WococoSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineRococoFinalityToWococo::new(target_client, target_sign),
finality_pipeline: FinalityPipelineRococoFinalityToWococo::new(
target_client,
target_sign,
),
}
}
}
@@ -48,7 +53,8 @@ impl RococoFinalityToWococo {
impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
type FinalitySyncPipeline = FinalityPipelineRococoFinalityToWococo;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
type TargetChain = Wococo;
@@ -25,16 +25,22 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
use relay_rococo_client::{
HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams,
};
use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use relay_wococo_client::{
HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo,
};
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Rococo-to-Wococo message lane.
pub type MessageLaneRococoMessagesToWococo =
@@ -48,23 +54,30 @@ pub struct RococoMessagesToWococo {
impl SubstrateMessageLane for RococoMessagesToWococo {
type MessageLane = MessageLaneRococoMessagesToWococo;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_wococo::TO_WOCOCO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_wococo::TO_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_wococo::TO_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rococo::FROM_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_rococo::FROM_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_rococo::FROM_ROCOCO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_rococo::FROM_ROCOCO_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_rococo::FROM_ROCOCO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Rococo;
type TargetChain = Wococo;
@@ -115,11 +128,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call = relay_wococo_client::runtime::Call::BridgeMessagesRococo(
@@ -178,14 +187,14 @@ pub async fn run(
// we don't know exact weights of the Wococo runtime. So to guess weights we'll be using
// weights from Rialto and then simply dividing it by x2.
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>,
>(
bp_wococo::max_extrinsic_weight(),
bp_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
max_messages_in_single_batch / 2,
max_messages_weight_in_single_batch / 2,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
log::info!(
target: "bridge",
@@ -217,8 +226,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_wococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_wococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -35,7 +35,9 @@ impl CliChain for Westend {
0
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
_message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
Err("Sending messages from Westend is not yet supported.".into())
}
}
@@ -24,7 +24,9 @@ use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Westend-to-Millau finality sync pipeline.
pub(crate) type FinalityPipelineWestendFinalityToMillau =
@@ -38,7 +40,10 @@ pub(crate) struct WestendFinalityToMillau {
impl WestendFinalityToMillau {
pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineWestendFinalityToMillau::new(target_client, target_sign),
finality_pipeline: FinalityPipelineWestendFinalityToMillau::new(
target_client,
target_sign,
),
}
}
}
@@ -46,7 +51,8 @@ impl WestendFinalityToMillau {
impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
type FinalitySyncPipeline = FinalityPipelineWestendFinalityToMillau;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
type TargetChain = Millau;
@@ -32,41 +32,41 @@ impl CliEncodeCall for Wococo {
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_wococo_client::runtime::Call::System(relay_wococo_client::runtime::SystemCall::remark(
Call::Remark { remark_payload, .. } => relay_wococo_client::runtime::Call::System(
relay_wococo_client::runtime::SystemCall::remark(
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
))
}
Call::BridgeSendMessage {
lane,
payload,
fee,
bridge_instance_index,
} => match *bridge_instance_index {
bridge::WOCOCO_TO_ROCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_wococo_client::runtime::Call::BridgeMessagesRococo(
relay_wococo_client::runtime::BridgeMessagesRococoCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
),
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
bridge::WOCOCO_TO_ROCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_wococo_client::runtime::Call::BridgeMessagesRococo(
relay_wococo_client::runtime::BridgeMessagesRococoCall::send_message(
lane.0, payload, fee.0,
),
)
},
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("The call is not supported"),
})
}
fn get_dispatch_info(call: &relay_wococo_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
fn get_dispatch_info(
call: &relay_wococo_client::runtime::Call,
) -> anyhow::Result<DispatchInfo> {
match *call {
relay_wococo_client::runtime::Call::System(relay_wococo_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
relay_wococo_client::runtime::Call::System(
relay_wococo_client::runtime::SystemCall::remark(_),
) => Ok(DispatchInfo {
weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
}),
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
}
}
@@ -86,7 +86,9 @@ impl CliChain for Wococo {
bp_wococo::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
fn encode_message(
_message: encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String> {
Err("Sending messages from Wococo is not yet supported.".into())
}
}
@@ -24,7 +24,9 @@ use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams};
use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use substrate_relay_helper::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate,
};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
@@ -45,7 +47,10 @@ pub(crate) struct WococoFinalityToRococo {
impl WococoFinalityToRococo {
pub fn new(target_client: Client<Rococo>, target_sign: RococoSigningParams) -> Self {
Self {
finality_pipeline: FinalityPipelineWococoFinalityToRococo::new(target_client, target_sign),
finality_pipeline: FinalityPipelineWococoFinalityToRococo::new(
target_client,
target_sign,
),
}
}
}
@@ -53,7 +58,8 @@ impl WococoFinalityToRococo {
impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
type FinalitySyncPipeline = FinalityPipelineWococoFinalityToRococo;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
type TargetChain = Rococo;
@@ -113,8 +119,10 @@ mod tests {
#[test]
fn maximal_balance_decrease_per_day_is_sane() {
// we expect Wococo -> Rococo relay to be running in all-headers mode
let maximal_balance_decrease =
compute_maximal_balance_decrease_per_day::<bp_kusama::Balance, bp_kusama::WeightToFee>(bp_wococo::DAYS);
let maximal_balance_decrease = compute_maximal_balance_decrease_per_day::<
bp_kusama::Balance,
bp_kusama::WeightToFee,
>(bp_wococo::DAYS);
assert!(
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_balance_decrease,
"Maximal expected loss per day {} is larger than hardcoded {}",
@@ -25,16 +25,22 @@ use bp_messages::MessageNonce;
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
use relay_rococo_client::{
HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams,
};
use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction};
use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
use substrate_relay_helper::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
SubstrateMessageLaneToSubstrate,
use relay_wococo_client::{
HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo,
};
use substrate_relay_helper::{
messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics,
SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
},
messages_source::SubstrateMessagesSource,
messages_target::SubstrateMessagesTarget,
};
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
/// Wococo-to-Rococo message lane.
pub type MessageLaneWococoMessagesToRococo =
@@ -47,23 +53,30 @@ pub struct WococoMessagesToRococo {
impl SubstrateMessageLane for WococoMessagesToRococo {
type MessageLane = MessageLaneWococoMessagesToRococo;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str =
bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_rococo::TO_ROCOCO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rococo::TO_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_rococo::TO_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_wococo::FROM_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
bp_wococo::FROM_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_wococo::FROM_WOCOCO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_wococo::FROM_WOCOCO_UNREWARDED_RELAYERS_STATE;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str =
bp_wococo::FROM_WOCOCO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME;
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight =
bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
type SourceChain = Wococo;
type TargetChain = Rococo;
@@ -114,11 +127,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes {
let (dispatch_weight, proof) = proof;
let FromBridgedChainMessagesProof {
ref nonces_start,
ref nonces_end,
..
} = proof;
let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof;
let messages_count = nonces_end - nonces_start + 1;
let call = relay_rococo_client::runtime::Call::BridgeMessagesWococo(
@@ -177,14 +186,14 @@ pub async fn run(
// we don't know exact weights of the Rococo runtime. So to guess weights we'll be using
// weights from Rialto and then simply dividing it by x2.
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
select_delivery_transaction_limits::<
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>,
>(
bp_rococo::max_extrinsic_weight(),
bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
max_messages_in_single_batch / 2,
max_messages_weight_in_single_batch / 2,
);
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
log::info!(
target: "bridge",
@@ -216,8 +225,10 @@ pub async fn run(
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
stall_timeout,
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
max_unrewarded_relayer_entries_at_target: bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_unrewarded_relayer_entries_at_target:
bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target:
bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
@@ -14,8 +14,10 @@
// 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::cli::{bridge::FullBridge, AccountId};
use crate::select_full_bridge;
use crate::{
cli::{bridge::FullBridge, AccountId},
select_full_bridge,
};
use relay_substrate_client::Chain;
use structopt::StructOpt;
use strum::VariantNames;
@@ -55,11 +57,7 @@ impl DeriveAccount {
select_full_bridge!(self.bridge, {
let (account, derived_account) = self.derive_account();
println!("Source address:\n{} ({})", account, Source::NAME);
println!(
"->Corresponding (derived) address:\n{} ({})",
derived_account,
Target::NAME,
);
println!("->Corresponding (derived) address:\n{} ({})", derived_account, Target::NAME,);
Ok(())
})
@@ -14,9 +14,12 @@
// 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::cli::bridge::FullBridge;
use crate::cli::{AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId};
use crate::select_full_bridge;
use crate::{
cli::{
bridge::FullBridge, AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId,
},
select_full_bridge,
};
use frame_support::weights::DispatchInfo;
use relay_substrate_client::Chain;
use structopt::StructOpt;
@@ -126,31 +129,30 @@ pub(crate) fn preprocess_call<Source: CliEncodeCall + CliChain, Target: CliEncod
bridge_instance: u8,
) {
match *call {
Call::Raw { .. } => {}
Call::Remark {
ref remark_size,
ref mut remark_payload,
} => {
Call::Raw { .. } => {},
Call::Remark { ref remark_size, ref mut remark_payload } =>
if remark_payload.is_none() {
*remark_payload = Some(HexBytes(generate_remark_payload(
remark_size,
compute_maximal_message_arguments_size(Source::max_extrinsic_size(), Target::max_extrinsic_size()),
compute_maximal_message_arguments_size(
Source::max_extrinsic_size(),
Target::max_extrinsic_size(),
),
)));
}
}
},
Call::Transfer { ref mut recipient, .. } => {
recipient.enforce_chain::<Source>();
}
Call::BridgeSendMessage {
ref mut bridge_instance_index,
..
} => {
},
Call::BridgeSendMessage { ref mut bridge_instance_index, .. } => {
*bridge_instance_index = bridge_instance;
}
},
};
}
fn generate_remark_payload(remark_size: &Option<ExplicitOrMaximal<usize>>, maximal_allowed_size: u32) -> Vec<u8> {
fn generate_remark_payload(
remark_size: &Option<ExplicitOrMaximal<usize>>,
maximal_allowed_size: u32,
) -> Vec<u8> {
match remark_size {
Some(ExplicitOrMaximal::Explicit(remark_size)) => vec![0; *remark_size],
Some(ExplicitOrMaximal::Maximal) => vec![0; maximal_allowed_size as _],
@@ -172,9 +174,11 @@ pub(crate) fn compute_maximal_message_arguments_size(
) -> u32 {
// assume that both signed extensions and other arguments fit 1KB
let service_tx_bytes_on_source_chain = 1024;
let maximal_source_extrinsic_size = maximal_source_extrinsic_size - service_tx_bytes_on_source_chain;
let maximal_call_size =
bridge_runtime_common::messages::target::maximal_incoming_message_size(maximal_target_extrinsic_size);
let maximal_source_extrinsic_size =
maximal_source_extrinsic_size - service_tx_bytes_on_source_chain;
let maximal_call_size = bridge_runtime_common::messages::target::maximal_incoming_message_size(
maximal_target_extrinsic_size,
);
let maximal_call_size = if maximal_call_size > maximal_source_extrinsic_size {
maximal_source_extrinsic_size
} else {
@@ -217,7 +221,8 @@ mod tests {
#[test]
fn should_encode_remark_with_default_payload() {
// given
let mut encode_call = EncodeCall::from_iter(vec!["encode-call", "rialto-to-millau", "remark"]);
let mut encode_call =
EncodeCall::from_iter(vec!["encode-call", "rialto-to-millau", "remark"]);
// when
let hex = encode_call.encode().unwrap();
@@ -247,8 +252,13 @@ mod tests {
#[test]
fn should_encode_remark_with_size() {
// given
let mut encode_call =
EncodeCall::from_iter(vec!["encode-call", "rialto-to-millau", "remark", "--remark-size", "12"]);
let mut encode_call = EncodeCall::from_iter(vec![
"encode-call",
"rialto-to-millau",
"remark",
"--remark-size",
"12",
]);
// when
let hex = encode_call.encode().unwrap();
@@ -275,7 +285,10 @@ mod tests {
assert_eq!(err.kind, structopt::clap::ErrorKind::ArgumentConflict);
let info = err.info.unwrap();
assert!(info.contains(&"remark-payload".to_string()) | info.contains(&"remark-size".to_string()))
assert!(
info.contains(&"remark-payload".to_string()) |
info.contains(&"remark-size".to_string())
)
}
#[test]
@@ -14,8 +14,10 @@
// 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::cli::{bridge::FullBridge, AccountId, CliChain, HexBytes};
use crate::select_full_bridge;
use crate::{
cli::{bridge::FullBridge, AccountId, CliChain, HexBytes},
select_full_bridge,
};
use structopt::StructOpt;
use strum::VariantNames;
@@ -52,7 +54,8 @@ impl EncodeMessage {
/// Run the command.
pub fn encode(self) -> anyhow::Result<HexBytes> {
select_full_bridge!(self.bridge, {
let payload = Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?;
let payload =
Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?;
Ok(HexBytes::encode(&payload))
})
}
@@ -74,7 +77,8 @@ mod tests {
fn should_encode_raw_message() {
// given
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000";
let encode_message = EncodeMessage::from_iter(vec!["encode-message", "rialto-to-millau", "raw", msg]);
let encode_message =
EncodeMessage::from_iter(vec!["encode-message", "rialto-to-millau", "raw", msg]);
// when
let hex = encode_message.encode().unwrap();
@@ -14,9 +14,10 @@
// 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::cli::bridge::FullBridge;
use crate::cli::{Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams};
use crate::select_full_bridge;
use crate::{
cli::{bridge::FullBridge, Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams},
select_full_bridge,
};
use bp_runtime::BalanceOf;
use codec::{Decode, Encode};
use relay_substrate_client::Chain;
@@ -42,21 +43,21 @@ pub struct EstimateFee {
impl EstimateFee {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
let Self {
source,
bridge,
lane,
payload,
} = self;
let Self { source, bridge, lane, payload } = self;
select_full_bridge!(bridge, {
let source_client = source.to_client::<Source>().await?;
let lane = lane.into();
let payload = Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
let payload =
Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
let fee: BalanceOf<Source> =
estimate_message_delivery_and_dispatch_fee(&source_client, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload)
.await?;
let fee: BalanceOf<Source> = estimate_message_delivery_and_dispatch_fee(
&source_client,
ESTIMATE_MESSAGE_FEE_METHOD,
lane,
payload,
)
.await?;
log::info!(target: "bridge", "Fee: {:?}", Balance(fee as _));
println!("{}", fee);
@@ -74,10 +75,11 @@ pub(crate) async fn estimate_message_delivery_and_dispatch_fee<Fee: Decode, C: C
let encoded_response = client
.state_call(estimate_fee_method.into(), (lane, payload).encode().into(), None)
.await?;
let decoded_response: Option<Fee> =
Decode::decode(&mut &encoded_response.0[..]).map_err(relay_substrate_client::Error::ResponseParseFailed)?;
let fee = decoded_response
.ok_or_else(|| anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec())))?;
let decoded_response: Option<Fee> = Decode::decode(&mut &encoded_response.0[..])
.map_err(relay_substrate_client::Error::ResponseParseFailed)?;
let fee = decoded_response.ok_or_else(|| {
anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec()))
})?;
Ok(fee)
}
@@ -67,7 +67,7 @@ macro_rules! select_bridge {
}
$generic
}
},
InitBridgeName::RialtoToMillau => {
type Source = relay_rialto_client::Rialto;
type Target = relay_millau_client::Millau;
@@ -83,7 +83,7 @@ macro_rules! select_bridge {
}
$generic
}
},
InitBridgeName::WestendToMillau => {
type Source = relay_westend_client::Westend;
type Target = relay_millau_client::Millau;
@@ -91,9 +91,10 @@ macro_rules! select_bridge {
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
// at Westend -> Millau initialization we're not using sudo, because otherwise our deployments
// may fail, because we need to initialize both Rialto -> Millau and Westend -> Millau bridge.
// => since there's single possible sudo account, one of transaction may fail with duplicate nonce error
// at Westend -> Millau initialization we're not using sudo, because otherwise
// our deployments may fail, because we need to initialize both Rialto -> Millau
// and Westend -> Millau bridge. => since there's single possible sudo account,
// one of transaction may fail with duplicate nonce error
millau_runtime::BridgeGrandpaWestendCall::<
millau_runtime::Runtime,
millau_runtime::WestendGrandpaInstance,
@@ -102,7 +103,7 @@ macro_rules! select_bridge {
}
$generic
}
},
InitBridgeName::RococoToWococo => {
type Source = relay_rococo_client::Rococo;
type Target = relay_wococo_client::Wococo;
@@ -111,12 +112,14 @@ macro_rules! select_bridge {
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
relay_wococo_client::runtime::Call::BridgeGrandpaRococo(
relay_wococo_client::runtime::BridgeGrandpaRococoCall::initialize(init_data),
relay_wococo_client::runtime::BridgeGrandpaRococoCall::initialize(
init_data,
),
)
}
$generic
}
},
InitBridgeName::WococoToRococo => {
type Source = relay_wococo_client::Wococo;
type Target = relay_rococo_client::Rococo;
@@ -125,12 +128,14 @@ macro_rules! select_bridge {
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
relay_rococo_client::runtime::Call::BridgeGrandpaWococo(
relay_rococo_client::runtime::BridgeGrandpaWococoCall::initialize(init_data),
relay_rococo_client::runtime::BridgeGrandpaWococoCall::initialize(
init_data,
),
)
}
$generic
}
},
InitBridgeName::KusamaToPolkadot => {
type Source = relay_kusama_client::Kusama;
type Target = relay_polkadot_client::Polkadot;
@@ -139,12 +144,14 @@ macro_rules! select_bridge {
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa(
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::initialize(init_data),
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::initialize(
init_data,
),
)
}
$generic
}
},
InitBridgeName::PolkadotToKusama => {
type Source = relay_polkadot_client::Polkadot;
type Target = relay_kusama_client::Kusama;
@@ -153,12 +160,14 @@ macro_rules! select_bridge {
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
relay_kusama_client::runtime::Call::BridgePolkadotGrandpa(
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::initialize(init_data),
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::initialize(
init_data,
),
)
}
$generic
}
},
}
};
}
@@ -181,7 +190,10 @@ impl InitBridge {
*target_client.genesis_hash(),
&target_sign,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(encode_init_bridge(initialization_data), transaction_nonce),
UnsignedTransaction::new(
encode_init_bridge(initialization_data),
transaction_nonce,
),
)
.encode(),
)
+17 -16
View File
@@ -86,7 +86,8 @@ pub enum Command {
EncodeMessage(encode_message::EncodeMessage),
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
EstimateFee(estimate_fee::EstimateFee),
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target
/// chain.
DeriveAccount(derive_account::DeriveAccount),
/// Resubmit transactions with increased tip if they are stalled.
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
@@ -100,12 +101,15 @@ impl Command {
use relay_utils::initialize::{initialize_logger, initialize_relay};
match self {
Self::RelayHeaders(_) | Self::RelayMessages(_) | Self::RelayHeadersAndMessages(_) | Self::InitBridge(_) => {
Self::RelayHeaders(_) |
Self::RelayMessages(_) |
Self::RelayHeadersAndMessages(_) |
Self::InitBridge(_) => {
initialize_relay();
}
},
_ => {
initialize_logger(false);
}
},
}
}
@@ -195,10 +199,7 @@ const SS58_FORMAT_PROOF: &str = "u16 -> Ss58Format is infallible; qed";
impl AccountId {
/// Create new SS58-formatted address from raw account id.
pub fn from_raw<T: CliChain>(account: sp_runtime::AccountId32) -> Self {
Self {
account,
ss58_format: T::ss58_format().try_into().expect(SS58_FORMAT_PROOF),
}
Self { account, ss58_format: T::ss58_format().try_into().expect(SS58_FORMAT_PROOF) }
}
/// Enforces formatting account to be for given [`CliChain`] type.
@@ -236,7 +237,7 @@ pub trait CliChain: relay_substrate_client::Chain {
/// Chain's current version of the runtime.
const RUNTIME_VERSION: sp_version::RuntimeVersion;
/// Crypto keypair type used to send messages.
/// Crypto KeyPair type used to send messages.
///
/// In case of chains supporting multiple cryptos, pick one used by the CLI.
type KeyPair: sp_core::crypto::Pair;
@@ -250,7 +251,9 @@ pub trait CliChain: relay_substrate_client::Chain {
fn ss58_format() -> u16;
/// Construct message payload to be sent over the bridge.
fn encode_message(message: crate::cli::encode_message::MessagePayload) -> Result<Self::MessagePayload, String>;
fn encode_message(
message: crate::cli::encode_message::MessagePayload,
) -> Result<Self::MessagePayload, String>;
/// Maximal extrinsic weight (from the runtime).
fn max_extrinsic_weight() -> Weight;
@@ -352,7 +355,7 @@ where
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "max" {
return Ok(ExplicitOrMaximal::Maximal);
return Ok(ExplicitOrMaximal::Maximal)
}
V::from_str(s)
@@ -531,10 +534,7 @@ mod tests {
let expected = vec![rialto1, rialto2, millau1, millau2];
// when
let parsed = expected
.iter()
.map(|s| AccountId::from_str(s).unwrap())
.collect::<Vec<_>>();
let parsed = expected.iter().map(|s| AccountId::from_str(s).unwrap()).collect::<Vec<_>>();
let actual = parsed.iter().map(|a| format!("{}", a)).collect::<Vec<_>>();
@@ -563,7 +563,8 @@ mod tests {
let alice = sp_core::sr25519::Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap();
let bob = sp_core::sr25519::Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap();
let bob_with_alice_password = sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap();
let bob_with_alice_password =
sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap();
let temp_dir = tempdir::TempDir::new("reads_suri_from_file").unwrap();
let mut suri_file_path = temp_dir.path().to_path_buf();
@@ -19,7 +19,9 @@ use strum::{EnumString, EnumVariantNames, VariantNames};
use substrate_relay_helper::finality_pipeline::SubstrateFinalitySyncPipeline;
use crate::cli::{PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams};
use crate::cli::{
PrometheusParams, SourceConnectionParams, TargetConnectionParams, TargetSigningParams,
};
/// Start headers relayer process.
#[derive(StructOpt)]
@@ -27,7 +29,8 @@ pub struct RelayHeaders {
/// A bridge instance to relay headers for.
#[structopt(possible_values = RelayHeadersBridge::VARIANTS, case_insensitive = true)]
bridge: RelayHeadersBridge,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) are relayed.
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
only_mandatory_headers: bool,
#[structopt(flatten)]
@@ -62,49 +65,49 @@ macro_rules! select_bridge {
type Finality = crate::chains::millau_headers_to_rialto::MillauFinalityToRialto;
$generic
}
},
RelayHeadersBridge::RialtoToMillau => {
type Source = relay_rialto_client::Rialto;
type Target = relay_millau_client::Millau;
type Finality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau;
$generic
}
},
RelayHeadersBridge::WestendToMillau => {
type Source = relay_westend_client::Westend;
type Target = relay_millau_client::Millau;
type Finality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau;
$generic
}
},
RelayHeadersBridge::RococoToWococo => {
type Source = relay_rococo_client::Rococo;
type Target = relay_wococo_client::Wococo;
type Finality = crate::chains::rococo_headers_to_wococo::RococoFinalityToWococo;
$generic
}
},
RelayHeadersBridge::WococoToRococo => {
type Source = relay_wococo_client::Wococo;
type Target = relay_rococo_client::Rococo;
type Finality = crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
$generic
}
},
RelayHeadersBridge::KusamaToPolkadot => {
type Source = relay_kusama_client::Kusama;
type Target = relay_polkadot_client::Polkadot;
type Finality = crate::chains::kusama_headers_to_polkadot::KusamaFinalityToPolkadot;
$generic
}
},
RelayHeadersBridge::PolkadotToKusama => {
type Source = relay_polkadot_client::Polkadot;
type Target = relay_kusama_client::Kusama;
type Finality = crate::chains::polkadot_headers_to_kusama::PolkadotFinalityToKusama;
$generic
}
},
}
};
}
@@ -27,14 +27,20 @@ use structopt::StructOpt;
use strum::VariantNames;
use codec::Encode;
use relay_substrate_client::{AccountIdOf, Chain, Client, TransactionSignScheme, UnsignedTransaction};
use relay_substrate_client::{
AccountIdOf, Chain, Client, TransactionSignScheme, UnsignedTransaction,
};
use relay_utils::metrics::MetricsParams;
use sp_core::{Bytes, Pair};
use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane};
use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
use substrate_relay_helper::{
messages_lane::{MessagesRelayParams, SubstrateMessageLane},
on_demand_headers::OnDemandHeadersRelay,
};
use crate::cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams};
use crate::declare_chain_options;
use crate::{
cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams},
declare_chain_options,
};
/// Maximal allowed conversion rate error ratio (abs(real - stored) / stored) that we allow.
///
@@ -63,16 +69,17 @@ pub struct HeadersAndMessagesSharedParams {
/// Create relayers fund accounts on both chains, if it does not exists yet.
#[structopt(long)]
create_relayers_fund_accounts: bool,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) are relayed.
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
only_mandatory_headers: bool,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
// The reason behind this macro is that 'normal' relays are using source and target chains terminology,
// which is unusable for both-way relays (if you're relaying headers from Rialto to Millau and from
// Millau to Rialto, then which chain is source?).
// The reason behind this macro is that 'normal' relays are using source and target chains
// terminology, which is unusable for both-way relays (if you're relaying headers from Rialto to
// Millau and from Millau to Rialto, then which chain is source?).
macro_rules! declare_bridge_options {
($chain1:ident, $chain2:ident) => {
paste::item! {
@@ -116,25 +123,35 @@ macro_rules! select_bridge {
type Left = relay_millau_client::Millau;
type Right = relay_rialto_client::Rialto;
type LeftToRightFinality = crate::chains::millau_headers_to_rialto::MillauFinalityToRialto;
type RightToLeftFinality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau;
type LeftToRightFinality =
crate::chains::millau_headers_to_rialto::MillauFinalityToRialto;
type RightToLeftFinality =
crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau;
type LeftToRightMessages = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
type RightToLeftMessages = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau;
type LeftToRightMessages =
crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
type RightToLeftMessages =
crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau;
type LeftAccountIdConverter = bp_millau::AccountIdConverter;
type RightAccountIdConverter = bp_rialto::AccountIdConverter;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_millau::BlockNumber = bp_millau::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_rialto::BlockNumber = bp_rialto::SESSION_LENGTH;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_millau::BlockNumber =
bp_millau::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_rialto::BlockNumber =
bp_rialto::SESSION_LENGTH;
use crate::chains::millau_messages_to_rialto::{
add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages,
update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate,
};
use crate::chains::rialto_messages_to_millau::{
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate,
use crate::chains::{
millau_messages_to_rialto::{
add_standalone_metrics as add_left_to_right_standalone_metrics,
run as left_to_right_messages,
update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate,
},
rialto_messages_to_millau::{
add_standalone_metrics as add_right_to_left_standalone_metrics,
run as right_to_left_messages,
update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate,
},
};
async fn left_create_account(
@@ -154,30 +171,40 @@ macro_rules! select_bridge {
}
$generic
}
},
RelayHeadersAndMessages::RococoWococo(_) => {
type Params = RococoWococoHeadersAndMessages;
type Left = relay_rococo_client::Rococo;
type Right = relay_wococo_client::Wococo;
type LeftToRightFinality = crate::chains::rococo_headers_to_wococo::RococoFinalityToWococo;
type RightToLeftFinality = crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
type LeftToRightFinality =
crate::chains::rococo_headers_to_wococo::RococoFinalityToWococo;
type RightToLeftFinality =
crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
type LeftToRightMessages = crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
type RightToLeftMessages = crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo;
type LeftToRightMessages =
crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
type RightToLeftMessages =
crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo;
type LeftAccountIdConverter = bp_rococo::AccountIdConverter;
type RightAccountIdConverter = bp_wococo::AccountIdConverter;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_rococo::BlockNumber = bp_rococo::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_wococo::BlockNumber = bp_wococo::SESSION_LENGTH;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_rococo::BlockNumber =
bp_rococo::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_wococo::BlockNumber =
bp_wococo::SESSION_LENGTH;
use crate::chains::rococo_messages_to_wococo::{
add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages,
};
use crate::chains::wococo_messages_to_rococo::{
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
use crate::chains::{
rococo_messages_to_wococo::{
add_standalone_metrics as add_left_to_right_standalone_metrics,
run as left_to_right_messages,
},
wococo_messages_to_rococo::{
add_standalone_metrics as add_right_to_left_standalone_metrics,
run as right_to_left_messages,
},
};
async fn update_right_to_left_conversion_rate(
@@ -213,32 +240,42 @@ macro_rules! select_bridge {
}
$generic
}
},
RelayHeadersAndMessages::KusamaPolkadot(_) => {
type Params = KusamaPolkadotHeadersAndMessages;
type Left = relay_kusama_client::Kusama;
type Right = relay_polkadot_client::Polkadot;
type LeftToRightFinality = crate::chains::kusama_headers_to_polkadot::KusamaFinalityToPolkadot;
type RightToLeftFinality = crate::chains::polkadot_headers_to_kusama::PolkadotFinalityToKusama;
type LeftToRightFinality =
crate::chains::kusama_headers_to_polkadot::KusamaFinalityToPolkadot;
type RightToLeftFinality =
crate::chains::polkadot_headers_to_kusama::PolkadotFinalityToKusama;
type LeftToRightMessages = crate::chains::kusama_messages_to_polkadot::KusamaMessagesToPolkadot;
type RightToLeftMessages = crate::chains::polkadot_messages_to_kusama::PolkadotMessagesToKusama;
type LeftToRightMessages =
crate::chains::kusama_messages_to_polkadot::KusamaMessagesToPolkadot;
type RightToLeftMessages =
crate::chains::polkadot_messages_to_kusama::PolkadotMessagesToKusama;
type LeftAccountIdConverter = bp_kusama::AccountIdConverter;
type RightAccountIdConverter = bp_polkadot::AccountIdConverter;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_kusama::BlockNumber = bp_kusama::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_polkadot::BlockNumber = bp_polkadot::SESSION_LENGTH;
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_kusama::BlockNumber =
bp_kusama::SESSION_LENGTH;
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_polkadot::BlockNumber =
bp_polkadot::SESSION_LENGTH;
use crate::chains::kusama_messages_to_polkadot::{
add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages,
update_polkadot_to_kusama_conversion_rate as update_right_to_left_conversion_rate,
};
use crate::chains::polkadot_messages_to_kusama::{
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
update_kusama_to_polkadot_conversion_rate as update_left_to_right_conversion_rate,
use crate::chains::{
kusama_messages_to_polkadot::{
add_standalone_metrics as add_left_to_right_standalone_metrics,
run as left_to_right_messages,
update_polkadot_to_kusama_conversion_rate as update_right_to_left_conversion_rate,
},
polkadot_messages_to_kusama::{
add_standalone_metrics as add_right_to_left_standalone_metrics,
run as right_to_left_messages,
update_kusama_to_polkadot_conversion_rate as update_left_to_right_conversion_rate,
},
};
async fn left_create_account(
@@ -248,25 +285,24 @@ macro_rules! select_bridge {
) -> anyhow::Result<()> {
let left_genesis_hash = *left_client.genesis_hash();
left_client
.submit_signed_extrinsic(left_sign.public().into(), move |_, transaction_nonce| {
Bytes(
Left::sign_transaction(
left_genesis_hash,
&left_sign,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
relay_kusama_client::runtime::Call::Balances(
relay_kusama_client::runtime::BalancesCall::transfer(
bp_kusama::AccountAddress::Id(account_id),
bp_kusama::EXISTENTIAL_DEPOSIT.into(),
.submit_signed_extrinsic(
left_sign.public().into(),
move |_, transaction_nonce| {
Bytes(
Left::sign_transaction(left_genesis_hash, &left_sign, relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
relay_kusama_client::runtime::Call::Balances(
relay_kusama_client::runtime::BalancesCall::transfer(
bp_kusama::AccountAddress::Id(account_id),
bp_kusama::EXISTENTIAL_DEPOSIT.into(),
),
),
transaction_nonce,
),
transaction_nonce,
),
).encode()
)
.encode(),
)
})
},
)
.await
.map(drop)
.map_err(|e| anyhow::format_err!("{}", e))
@@ -279,32 +315,31 @@ macro_rules! select_bridge {
) -> anyhow::Result<()> {
let right_genesis_hash = *right_client.genesis_hash();
right_client
.submit_signed_extrinsic(right_sign.public().into(), move |_, transaction_nonce| {
Bytes(
Right::sign_transaction(
right_genesis_hash,
&right_sign,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
relay_polkadot_client::runtime::Call::Balances(
relay_polkadot_client::runtime::BalancesCall::transfer(
bp_polkadot::AccountAddress::Id(account_id),
bp_polkadot::EXISTENTIAL_DEPOSIT.into(),
.submit_signed_extrinsic(
right_sign.public().into(),
move |_, transaction_nonce| {
Bytes(
Right::sign_transaction(right_genesis_hash, &right_sign, relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(
relay_polkadot_client::runtime::Call::Balances(
relay_polkadot_client::runtime::BalancesCall::transfer(
bp_polkadot::AccountAddress::Id(account_id),
bp_polkadot::EXISTENTIAL_DEPOSIT.into(),
),
),
transaction_nonce,
),
transaction_nonce,
),
).encode()
)
.encode(),
)
})
},
)
.await
.map(drop)
.map_err(|e| anyhow::format_err!("{}", e))
}
$generic
}
},
}
};
}
@@ -330,16 +365,19 @@ impl RelayHeadersAndMessages {
let left_client = params.left.to_client::<Left>().await?;
let left_transactions_mortality = params.left_sign.transactions_mortality()?;
let left_sign = params.left_sign.to_keypair::<Left>()?;
let left_messages_pallet_owner = params.left_messages_pallet_owner.to_keypair::<Left>()?;
let left_messages_pallet_owner =
params.left_messages_pallet_owner.to_keypair::<Left>()?;
let right_client = params.right.to_client::<Right>().await?;
let right_transactions_mortality = params.right_sign.transactions_mortality()?;
let right_sign = params.right_sign.to_keypair::<Right>()?;
let right_messages_pallet_owner = params.right_messages_pallet_owner.to_keypair::<Right>()?;
let right_messages_pallet_owner =
params.right_messages_pallet_owner.to_keypair::<Right>()?;
let lanes = params.shared.lane;
let relayer_mode = params.shared.relayer_mode.into();
const METRIC_IS_SOME_PROOF: &str = "it is `None` when metric has been already registered; \
const METRIC_IS_SOME_PROOF: &str =
"it is `None` when metric has been already registered; \
this is the command entrypoint, so nothing has been registered yet; \
qed";
@@ -413,22 +451,40 @@ impl RelayHeadersAndMessages {
}
if params.shared.create_relayers_fund_accounts {
let relayer_fund_acount_id =
pallet_bridge_messages::relayer_fund_account_id::<AccountIdOf<Left>, LeftAccountIdConverter>();
let relayer_fund_acount_id = pallet_bridge_messages::relayer_fund_account_id::<
AccountIdOf<Left>,
LeftAccountIdConverter,
>();
let relayers_fund_account_balance =
left_client.free_native_balance(relayer_fund_acount_id.clone()).await;
if let Err(relay_substrate_client::Error::AccountDoesNotExist) = relayers_fund_account_balance {
if let Err(relay_substrate_client::Error::AccountDoesNotExist) =
relayers_fund_account_balance
{
log::info!(target: "bridge", "Going to create relayers fund account at {}.", Left::NAME);
left_create_account(left_client.clone(), left_sign.clone(), relayer_fund_acount_id).await?;
left_create_account(
left_client.clone(),
left_sign.clone(),
relayer_fund_acount_id,
)
.await?;
}
let relayer_fund_acount_id =
pallet_bridge_messages::relayer_fund_account_id::<AccountIdOf<Right>, RightAccountIdConverter>();
let relayer_fund_acount_id = pallet_bridge_messages::relayer_fund_account_id::<
AccountIdOf<Right>,
RightAccountIdConverter,
>();
let relayers_fund_account_balance =
right_client.free_native_balance(relayer_fund_acount_id.clone()).await;
if let Err(relay_substrate_client::Error::AccountDoesNotExist) = relayers_fund_account_balance {
if let Err(relay_substrate_client::Error::AccountDoesNotExist) =
relayers_fund_account_balance
{
log::info!(target: "bridge", "Going to create relayers fund account at {}.", Right::NAME);
right_create_account(right_client.clone(), right_sign.clone(), relayer_fund_acount_id).await?;
right_create_account(
right_client.clone(),
right_sign.clone(),
relayer_fund_acount_id,
)
.await?;
}
}
@@ -19,12 +19,13 @@ use strum::{EnumString, EnumVariantNames, VariantNames};
use substrate_relay_helper::messages_lane::MessagesRelayParams;
use crate::cli::bridge::FullBridge;
use crate::cli::{
HexLaneId, PrometheusParams, SourceConnectionParams, SourceSigningParams, TargetConnectionParams,
TargetSigningParams,
use crate::{
cli::{
bridge::FullBridge, HexLaneId, PrometheusParams, SourceConnectionParams,
SourceSigningParams, TargetConnectionParams, TargetSigningParams,
},
select_full_bridge,
};
use crate::select_full_bridge;
/// Relayer operating mode.
#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq)]
@@ -32,7 +33,8 @@ use crate::select_full_bridge;
pub enum RelayerMode {
/// The relayer doesn't care about rewards.
Altruistic,
/// The relayer will deliver all messages and confirmations as long as he's not losing any funds.
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
Rational,
}
@@ -18,7 +18,9 @@ use crate::cli::{TargetConnectionParams, TargetSigningParams};
use codec::{Decode, Encode};
use num_traits::{One, Zero};
use relay_substrate_client::{BlockWithJustification, Chain, Client, Error as SubstrateError, TransactionSignScheme};
use relay_substrate_client::{
BlockWithJustification, Chain, Client, Error as SubstrateError, TransactionSignScheme,
};
use relay_utils::FailedClient;
use sp_core::Bytes;
use sp_runtime::{
@@ -54,13 +56,15 @@ macro_rules! select_bridge {
type Target = relay_millau_client::Millau;
type TargetSign = relay_millau_client::Millau;
// When large message is being sent from Millau to Rialto AND other transactions are blocking
// it from being mined, we'll see something like this in logs:
// When large message is being sent from Millau to Rialto AND other transactions are
// blocking it from being mined, we'll see something like this in logs:
//
// Millau transaction priority with tip=0: 17800827994. Target priority: 526186677695
// Millau transaction priority with tip=0: 17800827994. Target priority:
// 526186677695
//
// So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then we need
// tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it up to `1_000_000_000_000`.
// So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then
// we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it
// up to `1_000_000_000_000`.
const TIP_STEP: bp_millau::Balance = 1_000_000_000;
const TIP_LIMIT: bp_millau::Balance = 1_000_000_000_000;
@@ -68,7 +72,7 @@ macro_rules! select_bridge {
const STALLED_BLOCKS: bp_millau::BlockNumber = 5;
$generic
}
},
}
};
}
@@ -158,8 +162,8 @@ async fn run_until_connection_lost<C: Chain, S: TransactionSignScheme<Chain = C>
C::NAME,
error,
);
return Err(FailedClient::Target);
}
return Err(FailedClient::Target)
},
};
}
}
@@ -174,8 +178,8 @@ async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>(
Some(original_transaction) => original_transaction,
None => {
log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME);
return Ok(context);
}
return Ok(context)
},
};
let original_transaction_hash = C::Hasher::hash(&original_transaction.encode());
let context = context.notice_transaction(original_transaction_hash);
@@ -189,15 +193,15 @@ async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>(
context.stalled_for,
context.stalled_for_limit,
);
return Ok(context);
return Ok(context)
}
let (best_block, target_priority) = match read_previous_best_priority::<C, S>(&client).await? {
Some((best_block, target_priority)) => (best_block, target_priority),
None => {
log::trace!(target: "bridge", "Failed to read priority of best {} transaction in its best block", C::NAME);
return Ok(context);
}
return Ok(context)
},
};
let (is_updated, updated_transaction) = select_transaction_tip::<C, S>(
@@ -213,7 +217,7 @@ async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>(
if !is_updated {
log::trace!(target: "bridge", "{} transaction tip can not be updated. Reached limit?", C::NAME);
return Ok(context);
return Ok(context)
}
let updated_transaction = updated_transaction.encode();
@@ -241,10 +245,10 @@ async fn lookup_signer_transaction<C: Chain, S: TransactionSignScheme<Chain = C>
let pending_transaction = S::SignedTransaction::decode(&mut &pending_transaction.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
if !S::is_signed_by(key_pair, &pending_transaction) {
continue;
continue
}
return Ok(Some(pending_transaction));
return Ok(Some(pending_transaction))
}
Ok(None)
@@ -286,14 +290,15 @@ async fn select_transaction_tip<C: Chain, S: TransactionSignScheme<Chain = C>>(
) -> Result<(bool, S::SignedTransaction), SubstrateError> {
let stx = format!("{:?}", tx);
let mut current_priority = client.validate_transaction(at_block, tx.clone()).await??.priority;
let mut unsigned_tx = S::parse_transaction(tx)
.ok_or_else(|| SubstrateError::Custom(format!("Failed to parse {} transaction {}", C::NAME, stx,)))?;
let mut unsigned_tx = S::parse_transaction(tx).ok_or_else(|| {
SubstrateError::Custom(format!("Failed to parse {} transaction {}", C::NAME, stx,))
})?;
let old_tip = unsigned_tx.tip;
while current_priority < target_priority {
let next_tip = unsigned_tx.tip + tip_step;
if next_tip > tip_limit {
break;
break
}
log::trace!(
@@ -14,12 +14,12 @@
// 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::cli::bridge::FullBridge;
use crate::cli::encode_call::{self, CliEncodeCall};
use crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee;
use crate::cli::{
Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams, SourceSigningParams,
TargetSigningParams,
bridge::FullBridge,
encode_call::{self, CliEncodeCall},
estimate_fee::estimate_message_delivery_and_dispatch_fee,
Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams,
SourceSigningParams, TargetSigningParams,
};
use bp_message_dispatch::{CallOrigin, MessagePayload};
use bp_runtime::BalanceOf;
@@ -77,7 +77,8 @@ pub struct SendMessage {
/// Dispatch weight of the message. If not passed, determined automatically.
#[structopt(long)]
dispatch_weight: Option<ExplicitOrMaximal<Weight>>,
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined automatically.
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined
/// automatically.
#[structopt(long)]
fee: Option<Balance>,
/// Message type.
@@ -138,7 +139,7 @@ impl SendMessage {
target_origin_public.into(),
digest_signature.into(),
)
}
},
},
&target_call,
*dispatch_fee_payment,
@@ -238,10 +239,7 @@ fn prepare_call_dispatch_weight(
weight_from_pre_dispatch_call: ExplicitOrMaximal<Weight>,
maximal_allowed_weight: Weight,
) -> Weight {
match user_specified_dispatch_weight
.clone()
.unwrap_or(weight_from_pre_dispatch_call)
{
match user_specified_dispatch_weight.clone().unwrap_or(weight_from_pre_dispatch_call) {
ExplicitOrMaximal::Explicit(weight) => weight,
ExplicitOrMaximal::Maximal => maximal_allowed_weight,
}
@@ -272,24 +270,14 @@ where
log::info!(target: "bridge", "Encoded Message Payload: {:?}", HexBytes::encode(&payload));
// re-pack to return `Vec<u8>`
let MessagePayload {
spec_version,
weight,
origin,
dispatch_fee_payment,
call,
} = payload;
MessagePayload {
spec_version,
weight,
origin,
dispatch_fee_payment,
call: call.0,
}
let MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call } = payload;
MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call: call.0 }
}
pub(crate) fn compute_maximal_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
bridge_runtime_common::messages::target::maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight)
bridge_runtime_common::messages::target::maximal_incoming_message_dispatch_weight(
maximal_extrinsic_weight,
)
}
#[cfg(test)]
@@ -321,7 +309,9 @@ mod tests {
MessagePayload {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 576000,
origin: CallOrigin::SourceAccount(sp_keyring::AccountKeyring::Alice.to_account_id()),
origin: CallOrigin::SourceAccount(
sp_keyring::AccountKeyring::Alice.to_account_id()
),
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain,
call: hex!("0001081234").to_vec(),
}
@@ -28,15 +28,16 @@ use strum::{EnumString, EnumVariantNames, VariantNames};
use frame_support::dispatch::GetDispatchInfo;
use relay_substrate_client::{
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances, Client,
Error as SubstrateError, HashOf, SignatureOf, Subscription, TransactionSignScheme, TransactionStatusOf,
UnsignedTransaction,
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances,
Client, Error as SubstrateError, HashOf, SignatureOf, Subscription, TransactionSignScheme,
TransactionStatusOf, UnsignedTransaction,
};
use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, H256, U256};
use sp_runtime::traits::{Convert, Header as HeaderT};
use crate::cli::{
Balance, CliChain, SourceConnectionParams, SourceSigningParams, TargetConnectionParams, TargetSigningParams,
Balance, CliChain, SourceConnectionParams, SourceSigningParams, TargetConnectionParams,
TargetSigningParams,
};
/// Swap tokens.
@@ -71,7 +72,8 @@ pub struct SwapTokens {
pub enum TokenSwapType {
/// The `target_sign` is temporary and only have funds for single swap.
NoLock,
/// This swap type prevents `source_signer` from restarting the swap after it has been completed.
/// This swap type prevents `source_signer` from restarting the swap after it has been
/// completed.
LockUntilBlock {
/// Number of blocks before the swap expires.
#[structopt(long)]
@@ -119,7 +121,7 @@ macro_rules! select_bridge {
const TARGET_TO_SOURCE_LANE_ID: bp_messages::LaneId = [0, 0, 0, 0];
$generic
}
},
}
};
}
@@ -133,7 +135,8 @@ impl SwapTokens {
let target_client = self.target.to_client::<Target>().await?;
let target_sign = self.target_sign.to_keypair::<Target>()?;
// names of variables in this function are matching names used by the `pallet-bridge-token-swap`
// names of variables in this function are matching names used by the
// `pallet-bridge-token-swap`
// prepare token swap intention
let token_swap = self
@@ -143,18 +146,25 @@ impl SwapTokens {
// group all accounts that will be used later
let accounts = TokenSwapAccounts {
source_account_at_bridged_chain: derive_target_account_from_source_account(
bp_runtime::SourceAccount::Account(token_swap.source_account_at_this_chain.clone()),
bp_runtime::SourceAccount::Account(
token_swap.source_account_at_this_chain.clone(),
),
),
target_account_at_this_chain: derive_source_account_from_target_account(
bp_runtime::SourceAccount::Account(token_swap.target_account_at_bridged_chain.clone()),
bp_runtime::SourceAccount::Account(
token_swap.target_account_at_bridged_chain.clone(),
),
),
source_account_at_this_chain: token_swap.source_account_at_this_chain.clone(),
target_account_at_bridged_chain: token_swap.target_account_at_bridged_chain.clone(),
swap_account: FromSwapToThisAccountIdConverter::convert(token_swap.using_encoded(blake2_256).into()),
swap_account: FromSwapToThisAccountIdConverter::convert(
token_swap.using_encoded(blake2_256).into(),
),
};
// account balances are used to demonstrate what's happening :)
let initial_balances = read_account_balances(&accounts, &source_client, &target_client).await?;
let initial_balances =
read_account_balances(&accounts, &source_client, &target_client).await?;
// before calling something that may fail, log what we're trying to do
log::info!(target: "bridge", "Starting swap: {:?}", token_swap);
@@ -171,7 +181,8 @@ impl SwapTokens {
token_swap.target_balance_at_bridged_chain,
)
.into();
let bridged_currency_transfer_weight = bridged_currency_transfer.get_dispatch_info().weight;
let bridged_currency_transfer_weight =
bridged_currency_transfer.get_dispatch_info().weight;
// sign message
let bridged_chain_spec_version = TARGET_SPEC_VERSION;
@@ -182,10 +193,12 @@ impl SwapTokens {
SOURCE_CHAIN_ID,
TARGET_CHAIN_ID,
);
let bridged_currency_transfer_signature: SignatureOf<Target> = target_sign.sign(&signature_payload).into();
let bridged_currency_transfer_signature: SignatureOf<Target> =
target_sign.sign(&signature_payload).into();
// prepare `create_swap` call
let target_public_at_bridged_chain: AccountPublicOf<Target> = target_sign.public().into();
let target_public_at_bridged_chain: AccountPublicOf<Target> =
target_sign.public().into();
let swap_delivery_and_dispatch_fee: BalanceOf<Source> =
crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee(
&source_client,
@@ -199,7 +212,8 @@ impl SwapTokens {
target_public_at_bridged_chain.clone(),
bridged_currency_transfer_signature.clone(),
),
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtTargetChain,
dispatch_fee_payment:
bp_runtime::messages::DispatchFeePayment::AtTargetChain,
call: bridged_currency_transfer.encode(),
},
)
@@ -245,19 +259,20 @@ impl SwapTokens {
pallet_bridge_token_swap::PENDING_SWAPS_MAP_NAME,
token_swap_hash.as_ref(),
);
match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key).await? {
match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key)
.await?
{
Some(bp_token_swap::TokenSwapState::Started) => {
log::info!(target: "bridge", "Swap has been successfully started");
let intermediate_balances =
read_account_balances(&accounts, &source_client, &target_client).await?;
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
}
Some(token_swap_state) => {
},
Some(token_swap_state) =>
return Err(anyhow::format_err!(
"Fresh token swap has unexpected state: {:?}",
token_swap_state,
))
}
)),
None => return Err(anyhow::format_err!("Failed to start token swap")),
};
@@ -265,7 +280,8 @@ impl SwapTokens {
// Step 2: message is being relayed to the target chain and dispathed there
//
// wait until message is dispatched at the target chain and dispatch result delivered back to source chain
// wait until message is dispatched at the target chain and dispatch result delivered
// back to source chain
let token_swap_state = wait_until_token_swap_state_is_changed(
&source_client,
&token_swap_storage_key,
@@ -275,32 +291,37 @@ impl SwapTokens {
let is_transfer_succeeded = match token_swap_state {
Some(bp_token_swap::TokenSwapState::Started) => {
unreachable!("wait_until_token_swap_state_is_changed only returns if state is not Started; qed",)
}
None => return Err(anyhow::format_err!("Fresh token swap has disappeared unexpectedly")),
},
None =>
return Err(anyhow::format_err!("Fresh token swap has disappeared unexpectedly")),
Some(bp_token_swap::TokenSwapState::Confirmed) => {
log::info!(
target: "bridge",
"Transfer has been successfully dispatched at the target chain. Swap can be claimed",
);
true
}
},
Some(bp_token_swap::TokenSwapState::Failed) => {
log::info!(
target: "bridge",
"Transfer has been dispatched with an error at the target chain. Swap can be canceled",
);
false
}
},
};
// by this time: (1) token swap account has been created and (2) if transfer has been successfully
// dispatched, both target chain balances have changed
let intermediate_balances = read_account_balances(&accounts, &source_client, &target_client).await?;
// by this time: (1) token swap account has been created and (2) if transfer has been
// successfully dispatched, both target chain balances have changed
let intermediate_balances =
read_account_balances(&accounts, &source_client, &target_client).await?;
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
// transfer has been dispatched, but we may need to wait until block where swap can be claimed/canceled
if let bp_token_swap::TokenSwapType::LockClaimUntilBlock(ref last_available_block_number, _) =
token_swap.swap_type
// transfer has been dispatched, but we may need to wait until block where swap can be
// claimed/canceled
if let bp_token_swap::TokenSwapType::LockClaimUntilBlock(
ref last_available_block_number,
_,
) = token_swap.swap_type
{
wait_until_swap_unlocked(
&source_client,
@@ -317,7 +338,8 @@ impl SwapTokens {
log::info!(target: "bridge", "Claiming the swap swap");
// prepare `claim_swap` message that will be sent over the bridge
let claim_swap_call: CallOf<Source> = pallet_bridge_token_swap::Call::claim_swap(token_swap).into();
let claim_swap_call: CallOf<Source> =
pallet_bridge_token_swap::Call::claim_swap(token_swap).into();
let claim_swap_message = bp_message_dispatch::MessagePayload {
spec_version: SOURCE_SPEC_VERSION,
weight: claim_swap_call.get_dispatch_info().weight,
@@ -354,7 +376,10 @@ impl SwapTokens {
target_genesis_hash,
&target_sign,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(send_message_call, transaction_nonce),
UnsignedTransaction::new(
send_message_call,
transaction_nonce,
),
)
.encode(),
)
@@ -374,7 +399,7 @@ impl SwapTokens {
if token_swap_state != None {
return Err(anyhow::format_err!(
"Confirmed token swap state has been changed to {:?} unexpectedly"
));
))
}
} else {
log::info!(target: "bridge", "Cancelling the swap");
@@ -390,7 +415,10 @@ impl SwapTokens {
source_genesis_hash,
&source_sign,
relay_substrate_client::TransactionEra::immortal(),
UnsignedTransaction::new(cancel_swap_call, transaction_nonce),
UnsignedTransaction::new(
cancel_swap_call,
transaction_nonce,
),
)
.encode(),
)
@@ -402,7 +430,8 @@ impl SwapTokens {
}
// print final balances
let final_balances = read_account_balances(&accounts, &source_client, &target_client).await?;
let final_balances =
read_account_balances(&accounts, &source_client, &target_client).await?;
log::info!(target: "bridge", "Final account balances: {:?}", final_balances);
Ok(())
@@ -454,22 +483,18 @@ impl SwapTokens {
source_client: &Client<Source>,
) -> anyhow::Result<bp_token_swap::TokenSwapType<BlockNumberOf<Source>>> {
match self.swap_type {
TokenSwapType::NoLock => Ok(bp_token_swap::TokenSwapType::TemporaryTargetAccountAtBridgedChain),
TokenSwapType::LockUntilBlock {
blocks_before_expire,
ref swap_nonce,
} => {
TokenSwapType::NoLock =>
Ok(bp_token_swap::TokenSwapType::TemporaryTargetAccountAtBridgedChain),
TokenSwapType::LockUntilBlock { blocks_before_expire, ref swap_nonce } => {
let blocks_before_expire: BlockNumberOf<Source> = blocks_before_expire.into();
let current_source_block_number = *source_client.best_header().await?.number();
Ok(bp_token_swap::TokenSwapType::LockClaimUntilBlock(
current_source_block_number + blocks_before_expire,
swap_nonce.unwrap_or_else(|| {
U256::from(random::<u128>())
.overflowing_mul(U256::from(random::<u128>()))
.0
U256::from(random::<u128>()).overflowing_mul(U256::from(random::<u128>())).0
}),
))
}
},
}
}
}
@@ -551,17 +576,16 @@ async fn wait_until_transaction_is_finalized<C: Chain>(
loop {
let transaction_status = subscription.next().await?;
match transaction_status {
Some(TransactionStatusOf::<C>::FinalityTimeout(_))
| Some(TransactionStatusOf::<C>::Usurped(_))
| Some(TransactionStatusOf::<C>::Dropped)
| Some(TransactionStatusOf::<C>::Invalid)
| None => {
Some(TransactionStatusOf::<C>::FinalityTimeout(_)) |
Some(TransactionStatusOf::<C>::Usurped(_)) |
Some(TransactionStatusOf::<C>::Dropped) |
Some(TransactionStatusOf::<C>::Invalid) |
None =>
return Err(anyhow::format_err!(
"We've been waiting for finalization of {} transaction, but it now has the {:?} status",
C::NAME,
transaction_status,
))
}
)),
Some(TransactionStatusOf::<C>::Finalized(block_hash)) => {
log::trace!(
target: "bridge",
@@ -569,8 +593,8 @@ async fn wait_until_transaction_is_finalized<C: Chain>(
C::NAME,
block_hash,
);
return Ok(block_hash);
}
return Ok(block_hash)
},
_ => {
log::trace!(
target: "bridge",
@@ -578,7 +602,7 @@ async fn wait_until_transaction_is_finalized<C: Chain>(
C::NAME,
transaction_status,
);
}
},
}
}
}
@@ -597,9 +621,10 @@ async fn wait_until_token_swap_state_is_changed<C: Chain>(
let best_block_hash = client.block_hash_by_number(best_block).await?;
log::trace!(target: "bridge", "Inspecting {} block {}/{}", C::NAME, best_block, best_block_hash);
let token_swap_state = read_token_swap_state(client, best_block_hash, swap_state_storage_key).await?;
let token_swap_state =
read_token_swap_state(client, best_block_hash, swap_state_storage_key).await?;
match token_swap_state {
Some(new_token_swap_state) if new_token_swap_state == previous_token_swap_state => {}
Some(new_token_swap_state) if new_token_swap_state == previous_token_swap_state => {},
_ => {
log::trace!(
target: "bridge",
@@ -607,8 +632,8 @@ async fn wait_until_token_swap_state_is_changed<C: Chain>(
previous_token_swap_state,
token_swap_state,
);
return Ok(token_swap_state);
}
return Ok(token_swap_state)
},
}
}
}
@@ -625,7 +650,7 @@ async fn wait_until_swap_unlocked<C: Chain>(
let best_block = client.best_finalized_header_number().await?;
let best_block_hash = client.block_hash_by_number(best_block).await?;
if best_block >= required_block_number {
return Ok(());
return Ok(())
}
log::trace!(target: "bridge", "Skipping {} block {}/{}", C::NAME, best_block, best_block_hash);
@@ -638,7 +663,5 @@ async fn read_token_swap_state<C: Chain>(
at_block: C::Hash,
swap_state_storage_key: &StorageKey,
) -> anyhow::Result<Option<bp_token_swap::TokenSwapState>> {
Ok(client
.storage_value(swap_state_storage_key.clone(), Some(at_block))
.await?)
Ok(client.storage_value(swap_state_storage_key.clone(), Some(at_block)).await?)
}
+63 -36
View File
@@ -14,12 +14,14 @@
// 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::{
rpc::Ethereum,
types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SignedRawTx,
SyncState, Transaction, TransactionHash, H256, U256,
},
ConnectionParams, Error, Result,
};
use crate::{ConnectionParams, Error, Result};
use jsonrpsee_ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder};
use relay_utils::relay_loop::RECONNECT_DELAY;
@@ -57,15 +59,17 @@ impl Client {
}
}
/// Try to connect to Ethereum node. Returns Ethereum RPC client if connection has been established
/// or error otherwise.
/// Try to connect to Ethereum node. Returns Ethereum RPC client if connection has been
/// established or error otherwise.
pub async fn try_connect(params: ConnectionParams) -> Result<Self> {
let (tokio, client) = Self::build_client(&params).await?;
Ok(Self { tokio, client, params })
}
/// Build client to use in connection.
async fn build_client(params: &ConnectionParams) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
async fn build_client(
params: &ConnectionParams,
) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
let tokio = tokio::runtime::Runtime::new()?;
let uri = format!("ws://{}:{}", params.host, params.port);
let client = tokio
@@ -90,13 +94,14 @@ impl Client {
match Ethereum::syncing(&*client).await? {
SyncState::NotSyncing => Ok(()),
SyncState::Syncing(syncing) => {
let missing_headers = syncing.highest_block.saturating_sub(syncing.current_block);
let missing_headers =
syncing.highest_block.saturating_sub(syncing.current_block);
if missing_headers > MAJOR_SYNC_BLOCKS.into() {
return Err(Error::ClientNotSynced(missing_headers));
return Err(Error::ClientNotSynced(missing_headers))
}
Ok(())
}
},
}
})
.await
@@ -104,21 +109,26 @@ impl Client {
/// Estimate gas usage for the given call.
pub async fn estimate_gas(&self, call_request: CallRequest) -> Result<U256> {
self.jsonrpsee_execute(move |client| async move { Ok(Ethereum::estimate_gas(&*client, call_request).await?) })
.await
self.jsonrpsee_execute(move |client| async move {
Ok(Ethereum::estimate_gas(&*client, call_request).await?)
})
.await
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn best_block_number(&self) -> Result<u64> {
self.jsonrpsee_execute(move |client| async move { Ok(Ethereum::block_number(&*client).await?.as_u64()) })
.await
self.jsonrpsee_execute(move |client| async move {
Ok(Ethereum::block_number(&*client).await?.as_u64())
})
.await
}
/// Retrieve number of the best known block from the Ethereum node.
pub async fn header_by_number(&self, block_number: u64) -> Result<Header> {
self.jsonrpsee_execute(move |client| async move {
let get_full_tx_objects = false;
let header = Ethereum::get_block_by_number(&*client, block_number, get_full_tx_objects).await?;
let header =
Ethereum::get_block_by_number(&*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),
@@ -141,19 +151,28 @@ impl Client {
}
/// 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> {
pub async fn header_by_number_with_transactions(
&self,
number: u64,
) -> Result<HeaderWithTransactions> {
self.jsonrpsee_execute(move |client| async move {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_number_with_transactions(&*client, number, get_full_tx_objects).await?;
let header = Ethereum::get_block_by_number_with_transactions(
&*client,
number,
get_full_tx_objects,
)
.await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
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);
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);
return Err(Error::IncompleteTransaction)
}
Ok(header)
@@ -162,19 +181,25 @@ impl Client {
}
/// 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> {
pub async fn header_by_hash_with_transactions(
&self,
hash: H256,
) -> Result<HeaderWithTransactions> {
self.jsonrpsee_execute(move |client| async move {
let get_full_tx_objects = true;
let header = Ethereum::get_block_by_hash_with_transactions(&*client, hash, get_full_tx_objects).await?;
let header =
Ethereum::get_block_by_hash_with_transactions(&*client, hash, get_full_tx_objects)
.await?;
let is_complete_header = header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some();
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);
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);
return Err(Error::IncompleteTransaction)
}
Ok(header)
@@ -184,8 +209,10 @@ impl Client {
/// Retrieve transaction by its hash from Ethereum node.
pub async fn transaction_by_hash(&self, hash: H256) -> Result<Option<Transaction>> {
self.jsonrpsee_execute(move |client| async move { Ok(Ethereum::transaction_by_hash(&*client, hash).await?) })
.await
self.jsonrpsee_execute(move |client| async move {
Ok(Ethereum::transaction_by_hash(&*client, hash).await?)
})
.await
}
/// Retrieve transaction receipt by transaction hash.
@@ -198,9 +225,9 @@ impl Client {
/// Get the nonce of the given account.
pub async fn account_nonce(&self, address: Address) -> Result<U256> {
self.jsonrpsee_execute(
move |client| async move { Ok(Ethereum::get_transaction_count(&*client, address).await?) },
)
self.jsonrpsee_execute(move |client| async move {
Ok(Ethereum::get_transaction_count(&*client, address).await?)
})
.await
}
@@ -219,8 +246,10 @@ impl Client {
/// Call Ethereum smart contract.
pub async fn eth_call(&self, call_transaction: CallRequest) -> Result<Bytes> {
self.jsonrpsee_execute(move |client| async move { Ok(Ethereum::call(&*client, call_transaction).await?) })
.await
self.jsonrpsee_execute(move |client| async move {
Ok(Ethereum::call(&*client, call_transaction).await?)
})
.await
}
/// Execute jsonrpsee future in tokio context.
@@ -231,8 +260,6 @@ impl Client {
T: Send + 'static,
{
let client = self.client.clone();
self.tokio
.spawn(async move { make_jsonrpsee_future(client).await })
.await?
self.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await?
}
}
+4 -4
View File
@@ -73,10 +73,10 @@ impl MaybeConnectionError for Error {
fn is_connection_error(&self) -> bool {
matches!(
*self,
Error::RpcError(RpcError::Transport(_))
| Error::RpcError(RpcError::Internal(_))
| Error::RpcError(RpcError::RestartNeeded(_))
| Error::ClientNotSynced(_),
Error::RpcError(RpcError::Transport(_)) |
Error::RpcError(RpcError::Internal(_)) |
Error::RpcError(RpcError::RestartNeeded(_)) |
Error::ClientNotSynced(_),
)
}
}
+6 -7
View File
@@ -23,9 +23,11 @@ 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 use crate::{
client::Client,
error::{Error, Result},
sign::{sign_and_submit_transaction, SigningParams},
};
pub mod types;
@@ -40,9 +42,6 @@ pub struct ConnectionParams {
impl Default for ConnectionParams {
fn default() -> Self {
ConnectionParams {
host: "localhost".into(),
port: 8546,
}
ConnectionParams { host: "localhost".into(), port: 8546 }
}
}
+2 -2
View File
@@ -17,8 +17,8 @@
//! Ethereum node RPC interface.
use crate::types::{
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SyncState, Transaction, TransactionHash,
H256, U256, U64,
Address, Bytes, CallRequest, Header, HeaderWithTransactions, Receipt, SyncState, Transaction,
TransactionHash, H256, U256, U64,
};
jsonrpsee_proc_macros::rpc_client_api! {
+4 -2
View File
@@ -14,8 +14,10 @@
// 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 crate::{
types::{Address, CallRequest, U256},
Client, Result,
};
use bp_eth_poa::signatures::{secret_to_address, SignTransaction};
use hex_literal::hex;
use secp256k1::SecretKey;
+13 -8
View File
@@ -18,7 +18,8 @@
use codec::Encode;
use relay_substrate_client::{
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -75,7 +76,13 @@ impl TransactionSignScheme for Kusama {
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::new(
unsigned.call,
bp_kusama::SignedExtensions::new(bp_kusama::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
bp_kusama::SignedExtensions::new(
bp_kusama::VERSION,
era,
genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
@@ -98,17 +105,15 @@ impl TransactionSignScheme for Kusama {
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_kusama::AccountId::from(*signer.public().as_array_ref()).into())
.map(|(address, _, _)| {
*address == bp_kusama::AccountId::from(*signer.public().as_array_ref()).into()
})
.unwrap_or(false)
}
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: extra.nonce(),
tip: extra.tip(),
})
Some(UnsignedTransaction { call: tx.function, nonce: extra.nonce(), tip: extra.tip() })
}
}
+5 -3
View File
@@ -29,8 +29,8 @@ pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
/// Polkadot account ownership digest from Kusama.
///
/// The byte vector returned by this function should be signed with a Polkadot account private key.
/// This way, the owner of `kusama_account_id` on Kusama proves that the Polkadot account private key
/// is also under his control.
/// This way, the owner of `kusama_account_id` on Kusama proves that the Polkadot account private
/// key is also under his control.
pub fn kusama_to_polkadot_account_ownership_digest<Call, AccountId, SpecVersion>(
polkadot_call: &Call,
kusama_account_id: AccountId,
@@ -128,7 +128,9 @@ pub enum BridgePolkadotMessagesCall {
),
#[codec(index = 6)]
receive_messages_delivery_proof(
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_polkadot::Hash>,
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<
bp_polkadot::Hash,
>,
UnrewardedRelayersState,
),
}
+12 -7
View File
@@ -18,8 +18,8 @@
use codec::{Compact, Decode, Encode};
use relay_substrate_client::{
BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf,
TransactionSignScheme, UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -100,7 +100,12 @@ impl TransactionSignScheme for Millau {
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_runtime::UncheckedExtrinsic::new_signed(
call,
signer.into_account(),
signature.into(),
extra,
)
}
fn is_signed(tx: &Self::SignedTransaction) -> bool {
@@ -110,7 +115,9 @@ impl TransactionSignScheme for Millau {
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == millau_runtime::Address::from(*signer.public().as_array_ref()))
.map(|(address, _, _)| {
*address == millau_runtime::Address::from(*signer.public().as_array_ref())
})
.unwrap_or(false)
}
@@ -118,9 +125,7 @@ impl TransactionSignScheme for Millau {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..])
.ok()?
.into(),
nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..]).ok()?.into(),
tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.6.encode()[..])
.ok()?
.into(),
+13 -8
View File
@@ -18,7 +18,8 @@
use codec::Encode;
use relay_substrate_client::{
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -75,7 +76,13 @@ impl TransactionSignScheme for Polkadot {
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::new(
unsigned.call,
bp_polkadot::SignedExtensions::new(bp_polkadot::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
bp_polkadot::SignedExtensions::new(
bp_polkadot::VERSION,
era,
genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
@@ -98,17 +105,15 @@ impl TransactionSignScheme for Polkadot {
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_polkadot::AccountId::from(*signer.public().as_array_ref()).into())
.map(|(address, _, _)| {
*address == bp_polkadot::AccountId::from(*signer.public().as_array_ref()).into()
})
.unwrap_or(false)
}
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: extra.nonce(),
tip: extra.tip(),
})
Some(UnsignedTransaction { call: tx.function, nonce: extra.nonce(), tip: extra.tip() })
}
}
@@ -128,7 +128,9 @@ pub enum BridgeKusamaMessagesCall {
),
#[codec(index = 6)]
receive_messages_delivery_proof(
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_kusama::Hash>,
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<
bp_kusama::Hash,
>,
UnrewardedRelayersState,
),
}
+9 -6
View File
@@ -18,8 +18,8 @@
use codec::{Compact, Decode, Encode};
use relay_substrate_client::{
BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf,
TransactionSignScheme, UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -100,7 +100,12 @@ impl TransactionSignScheme for Rialto {
let signer: sp_runtime::MultiSigner = signer.public().into();
let (call, extra, _) = raw_payload.deconstruct();
rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account().into(), signature.into(), extra)
rialto_runtime::UncheckedExtrinsic::new_signed(
call,
signer.into_account().into(),
signature.into(),
extra,
)
}
fn is_signed(tx: &Self::SignedTransaction) -> bool {
@@ -118,9 +123,7 @@ impl TransactionSignScheme for Rialto {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..])
.ok()?
.into(),
nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..]).ok()?.into(),
tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.6.encode()[..])
.ok()?
.into(),
+13 -8
View File
@@ -18,7 +18,8 @@
use codec::Encode;
use relay_substrate_client::{
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -78,7 +79,13 @@ impl TransactionSignScheme for Rococo {
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::new(
unsigned.call,
bp_rococo::SignedExtensions::new(bp_rococo::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
bp_rococo::SignedExtensions::new(
bp_rococo::VERSION,
era,
genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
@@ -101,17 +108,15 @@ impl TransactionSignScheme for Rococo {
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_rococo::AccountId::from(*signer.public().as_array_ref()).into())
.map(|(address, _, _)| {
*address == bp_rococo::AccountId::from(*signer.public().as_array_ref()).into()
})
.unwrap_or(false)
}
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: extra.nonce(),
tip: extra.tip(),
})
Some(UnsignedTransaction { call: tx.function, nonce: extra.nonce(), tip: extra.tip() })
}
}
+3 -1
View File
@@ -115,7 +115,9 @@ pub enum BridgeMessagesWococoCall {
),
#[codec(index = 6)]
receive_messages_delivery_proof(
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_wococo::Hash>,
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<
bp_wococo::Hash,
>,
UnrewardedRelayersState,
),
}
+1 -5
View File
@@ -92,11 +92,7 @@ pub struct UnsignedTransaction<C: Chain> {
impl<C: Chain> UnsignedTransaction<C> {
/// Create new unsigned transaction with given call, nonce and zero tip.
pub fn new(call: C::Call, nonce: C::Index) -> Self {
Self {
call,
nonce,
tip: Zero::zero(),
}
Self { call, nonce, tip: Zero::zero() }
}
/// Set transaction tip.
+91 -61
View File
@@ -16,17 +16,21 @@
//! Substrate node client.
use crate::chain::{Chain, ChainWithBalances, TransactionStatusOf};
use crate::rpc::Substrate;
use crate::{ConnectionParams, Error, HashOf, HeaderIdOf, Result};
use crate::{
chain::{Chain, ChainWithBalances, TransactionStatusOf},
rpc::Substrate,
ConnectionParams, Error, HashOf, HeaderIdOf, Result,
};
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use codec::{Decode, Encode};
use frame_system::AccountInfo;
use futures::{SinkExt, StreamExt};
use jsonrpsee_ws_client::{traits::SubscriptionClient, v2::params::JsonRpcParams, DeserializeOwned};
use jsonrpsee_ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder};
use jsonrpsee_ws_client::{
traits::SubscriptionClient, v2::params::JsonRpcParams, DeserializeOwned, WsClient as RpcClient,
WsClientBuilder as RpcClientBuilder,
};
use num_traits::{Bounded, Zero};
use pallet_balances::AccountData;
use pallet_transaction_payment::InclusionFee;
@@ -62,9 +66,10 @@ pub struct Client<C: Chain> {
client: Arc<RpcClient>,
/// Genesis block hash.
genesis_hash: HashOf<C>,
/// If several tasks are submitting their transactions simultaneously using `submit_signed_extrinsic`
/// method, they may get the same transaction nonce. So one of transactions will be rejected
/// from the pool. This lock is here to prevent situations like that.
/// If several tasks are submitting their transactions simultaneously using
/// `submit_signed_extrinsic` method, they may get the same transaction nonce. So one of
/// transactions will be rejected from the pool. This lock is here to prevent situations like
/// that.
submit_signed_extrinsic_lock: Arc<Mutex<()>>,
}
@@ -94,9 +99,7 @@ impl<C: Chain> Clone for Client<C> {
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()
fmt.debug_struct("Client").field("genesis_hash", &self.genesis_hash).finish()
}
}
@@ -130,7 +133,9 @@ impl<C: Chain> Client<C> {
let number: C::BlockNumber = Zero::zero();
let genesis_hash_client = client.clone();
let genesis_hash = tokio
.spawn(async move { Substrate::<C>::chain_get_block_hash(&*genesis_hash_client, number).await })
.spawn(async move {
Substrate::<C>::chain_get_block_hash(&*genesis_hash_client, number).await
})
.await??;
Ok(Self {
@@ -143,7 +148,9 @@ impl<C: Chain> Client<C> {
}
/// Build client to use in connection.
async fn build_client(params: ConnectionParams) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
async fn build_client(
params: ConnectionParams,
) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
let tokio = tokio::runtime::Runtime::new()?;
let uri = format!(
"{}://{}:{}",
@@ -186,16 +193,15 @@ impl<C: Chain> Client<C> {
/// Return hash of the best finalized block.
pub async fn best_finalized_header_hash(&self) -> Result<C::Hash> {
self.jsonrpsee_execute(|client| async move { Ok(Substrate::<C>::chain_get_finalized_head(&*client).await?) })
.await
self.jsonrpsee_execute(|client| async move {
Ok(Substrate::<C>::chain_get_finalized_head(&*client).await?)
})
.await
}
/// Return number of the best finalized block.
pub async fn best_finalized_header_number(&self) -> Result<C::BlockNumber> {
Ok(*self
.header_by_hash(self.best_finalized_header_hash().await?)
.await?
.number())
Ok(*self.header_by_hash(self.best_finalized_header_hash().await?).await?.number())
}
/// Returns the best Substrate header.
@@ -203,15 +209,17 @@ impl<C: Chain> Client<C> {
where
C::Header: DeserializeOwned,
{
self.jsonrpsee_execute(|client| async move { Ok(Substrate::<C>::chain_get_header(&*client, None).await?) })
.await
self.jsonrpsee_execute(|client| async move {
Ok(Substrate::<C>::chain_get_header(&*client, None).await?)
})
.await
}
/// Get a Substrate block from its hash.
pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> {
self.jsonrpsee_execute(
move |client| async move { Ok(Substrate::<C>::chain_get_block(&*client, block_hash).await?) },
)
self.jsonrpsee_execute(move |client| async move {
Ok(Substrate::<C>::chain_get_block(&*client, block_hash).await?)
})
.await
}
@@ -246,8 +254,10 @@ impl<C: Chain> Client<C> {
/// Return runtime version.
pub async fn runtime_version(&self) -> Result<RuntimeVersion> {
self.jsonrpsee_execute(move |client| async move { Ok(Substrate::<C>::state_runtime_version(&*client).await?) })
.await
self.jsonrpsee_execute(move |client| async move {
Ok(Substrate::<C>::state_runtime_version(&*client).await?)
})
.await
}
/// Read value from runtime storage.
@@ -259,7 +269,9 @@ impl<C: Chain> Client<C> {
self.jsonrpsee_execute(move |client| async move {
Substrate::<C>::state_get_storage(&*client, storage_key, block_hash)
.await?
.map(|encoded_value| T::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed))
.map(|encoded_value| {
T::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed)
})
.transpose()
})
.await
@@ -272,12 +284,14 @@ impl<C: Chain> Client<C> {
{
self.jsonrpsee_execute(move |client| async move {
let storage_key = C::account_info_storage_key(&account);
let encoded_account_data = Substrate::<C>::state_get_storage(&*client, storage_key, None)
.await?
.ok_or(Error::AccountDoesNotExist)?;
let decoded_account_data =
AccountInfo::<C::Index, AccountData<C::Balance>>::decode(&mut &encoded_account_data.0[..])
.map_err(Error::ResponseParseFailed)?;
let encoded_account_data =
Substrate::<C>::state_get_storage(&*client, storage_key, None)
.await?
.ok_or(Error::AccountDoesNotExist)?;
let decoded_account_data = AccountInfo::<C::Index, AccountData<C::Balance>>::decode(
&mut &encoded_account_data.0[..],
)
.map_err(Error::ResponseParseFailed)?;
Ok(decoded_account_data.data.free)
})
.await
@@ -348,9 +362,8 @@ impl<C: Chain> Client<C> {
let subscription = client
.subscribe(
"author_submitAndWatchExtrinsic",
JsonRpcParams::Array(vec![
jsonrpsee_types::to_json_value(extrinsic).map_err(|e| Error::RpcError(e.into()))?
]),
JsonRpcParams::Array(vec![jsonrpsee_types::to_json_value(extrinsic)
.map_err(|e| Error::RpcError(e.into()))?]),
"author_unwatchExtrinsic",
)
.await?;
@@ -370,9 +383,9 @@ impl<C: Chain> Client<C> {
/// Returns pending extrinsics from transaction pool.
pub async fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
self.jsonrpsee_execute(
move |client| async move { Ok(Substrate::<C>::author_pending_extrinsics(&*client).await?) },
)
self.jsonrpsee_execute(move |client| async move {
Ok(Substrate::<C>::author_pending_extrinsics(&*client).await?)
})
.await
}
@@ -386,9 +399,10 @@ impl<C: Chain> Client<C> {
let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string();
let data = Bytes((TransactionSource::External, transaction, at_block).encode());
let encoded_response = Substrate::<C>::state_call(&*client, call, data, Some(at_block)).await?;
let validity =
TransactionValidity::decode(&mut &encoded_response.0[..]).map_err(Error::ResponseParseFailed)?;
let encoded_response =
Substrate::<C>::state_call(&*client, call, data, Some(at_block)).await?;
let validity = TransactionValidity::decode(&mut &encoded_response.0[..])
.map_err(Error::ResponseParseFailed)?;
Ok(validity)
})
@@ -396,9 +410,13 @@ impl<C: Chain> Client<C> {
}
/// Estimate fee that will be spent on given extrinsic.
pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result<InclusionFee<C::Balance>> {
pub async fn estimate_extrinsic_fee(
&self,
transaction: Bytes,
) -> Result<InclusionFee<C::Balance>> {
self.jsonrpsee_execute(move |client| async move {
let fee_details = Substrate::<C>::payment_query_fee_details(&*client, transaction, None).await?;
let fee_details =
Substrate::<C>::payment_query_fee_details(&*client, transaction, None).await?;
let inclusion_fee = fee_details
.inclusion_fee
.map(|inclusion_fee| InclusionFee {
@@ -406,8 +424,10 @@ impl<C: Chain> Client<C> {
.unwrap_or_else(|_| C::Balance::max_value()),
len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256())
.unwrap_or_else(|_| C::Balance::max_value()),
adjusted_weight_fee: C::Balance::try_from(inclusion_fee.adjusted_weight_fee.into_u256())
.unwrap_or_else(|_| C::Balance::max_value()),
adjusted_weight_fee: C::Balance::try_from(
inclusion_fee.adjusted_weight_fee.into_u256(),
)
.unwrap_or_else(|_| C::Balance::max_value()),
})
.unwrap_or_else(|| InclusionFee {
base_fee: Zero::zero(),
@@ -420,12 +440,16 @@ impl<C: Chain> Client<C> {
}
/// Get the GRANDPA authority set at given block.
pub async fn grandpa_authorities_set(&self, block: C::Hash) -> Result<OpaqueGrandpaAuthoritiesSet> {
pub async fn grandpa_authorities_set(
&self,
block: C::Hash,
) -> Result<OpaqueGrandpaAuthoritiesSet> {
self.jsonrpsee_execute(move |client| async move {
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
let data = Bytes(Vec::new());
let encoded_response = Substrate::<C>::state_call(&*client, call, data, Some(block)).await?;
let encoded_response =
Substrate::<C>::state_call(&*client, call, data, Some(block)).await?;
let authority_list = encoded_response.0;
Ok(authority_list)
@@ -434,7 +458,12 @@ impl<C: Chain> Client<C> {
}
/// Execute runtime call at given block.
pub async fn state_call(&self, method: String, data: Bytes, at_block: Option<C::Hash>) -> Result<Bytes> {
pub async fn state_call(
&self,
method: String,
data: Bytes,
at_block: Option<C::Hash>,
) -> Result<Bytes> {
self.jsonrpsee_execute(move |client| async move {
Substrate::<C>::state_call(&*client, method, data, at_block)
.await
@@ -444,7 +473,11 @@ impl<C: Chain> Client<C> {
}
/// Returns storage proof of given storage keys.
pub async fn prove_storage(&self, keys: Vec<StorageKey>, at_block: C::Hash) -> Result<StorageProof> {
pub async fn prove_storage(
&self,
keys: Vec<StorageKey>,
at_block: C::Hash,
) -> Result<StorageProof> {
self.jsonrpsee_execute(move |client| async move {
Substrate::<C>::state_prove_storage(&*client, keys, Some(at_block))
.await
@@ -485,9 +518,7 @@ impl<C: Chain> Client<C> {
T: Send + 'static,
{
let client = self.client.clone();
self.tokio
.spawn(async move { make_jsonrpsee_future(client).await })
.await?
self.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await?
}
}
@@ -508,11 +539,10 @@ impl<T: DeserializeOwned> Subscription<T> {
) {
loop {
match subscription.next().await {
Ok(Some(item)) => {
Ok(Some(item)) =>
if sender.send(Some(item)).await.is_err() {
break;
}
}
break
},
Ok(None) => {
log::trace!(
target: "bridge",
@@ -521,8 +551,8 @@ impl<T: DeserializeOwned> Subscription<T> {
item_type,
);
let _ = sender.send(None).await;
break;
}
break
},
Err(e) => {
log::trace!(
target: "bridge",
@@ -532,8 +562,8 @@ impl<T: DeserializeOwned> Subscription<T> {
e,
);
let _ = sender.send(None).await;
break;
}
break
},
}
}
}
+8 -6
View File
@@ -96,10 +96,10 @@ impl MaybeConnectionError for Error {
fn is_connection_error(&self) -> bool {
matches!(
*self,
Error::RpcError(RpcError::Transport(_))
| Error::RpcError(RpcError::Internal(_))
| Error::RpcError(RpcError::RestartNeeded(_))
| Error::ClientNotSynced(_),
Error::RpcError(RpcError::Transport(_)) |
Error::RpcError(RpcError::Internal(_)) |
Error::RpcError(RpcError::RestartNeeded(_)) |
Error::ClientNotSynced(_),
)
}
}
@@ -110,9 +110,11 @@ impl std::fmt::Display for Error {
Self::Io(e) => e.to_string(),
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::UninitializedBridgePallet =>
"The Substrate bridge pallet has not been initialized yet.".into(),
Self::AccountDoesNotExist => "Account does not exist on the chain".into(),
Self::MissingMandatoryCodeEntry => "Mandatory :code: entry is missing from runtime storage".into(),
Self::MissingMandatoryCodeEntry =>
"Mandatory :code: entry is missing from runtime storage".into(),
Self::StorageProofError(e) => format!("Error when parsing storage proof: {:?}", e),
Self::ClientNotSynced(health) => format!("Substrate client is not synced: {}", health),
Self::TransactionInvalid(e) => format!("Substrate transaction is invalid: {:?}", e),
@@ -16,10 +16,12 @@
//! 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 crate::{
chain::{BlockWithJustification, Chain},
client::Client,
error::Error,
sync_header::SyncHeader,
};
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
@@ -43,12 +45,11 @@ pub struct FinalitySource<C: Chain, P> {
impl<C: Chain, P> FinalitySource<C, P> {
/// Create new headers source using given client.
pub fn new(client: Client<C>, maximal_header_number: Option<RequiredHeaderNumberRef<C>>) -> Self {
FinalitySource {
client,
maximal_header_number,
_phantom: Default::default(),
}
pub fn new(
client: Client<C>,
maximal_header_number: Option<RequiredHeaderNumberRef<C>>,
) -> Self {
FinalitySource { client, maximal_header_number, _phantom: Default::default() }
}
/// Returns reference to the underlying RPC client.
@@ -122,7 +123,9 @@ where
let justification = signed_block
.justification()
.map(|raw_justification| GrandpaJustification::<C::Header>::decode(&mut raw_justification.as_slice()))
.map(|raw_justification| {
GrandpaJustification::<C::Header>::decode(&mut raw_justification.as_slice())
})
.transpose()
.map_err(Error::ResponseParseFailed)?;
@@ -155,11 +158,11 @@ where
Ok(j) => j,
Err(err) => {
log_error(format!("decode failed with error {:?}", err));
continue;
}
continue
},
};
return Some((justification, subscription));
return Some((justification, subscription))
}
},
)
+15 -18
View File
@@ -52,7 +52,10 @@ pub trait Environment<C: ChainWithBalances>: Send + Sync + 'static {
}
/// 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) {
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;
@@ -68,7 +71,7 @@ pub fn abort_on_spec_version_change<C: ChainWithBalances>(mut env: impl Environm
);
env.abort().await;
}
},
Err(error) => log::warn!(
target: "bridge-guard",
"Failed to read {} runtime version: {:?}. Relay may need to be stopped manually",
@@ -83,7 +86,8 @@ pub fn abort_on_spec_version_change<C: ChainWithBalances>(mut env: impl Environm
}
/// Abort if, during 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.
/// 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,
@@ -129,7 +133,7 @@ pub fn abort_when_account_balance_decreased<C: ChainWithBalances>(
env.abort().await;
}
}
},
Err(error) => {
log::warn!(
target: "bridge-guard",
@@ -138,7 +142,7 @@ pub fn abort_when_account_balance_decreased<C: ChainWithBalances>(
account_id,
error,
);
}
},
};
env.sleep(conditions_check_delay::<C>()).await;
@@ -158,9 +162,7 @@ impl<C: ChainWithBalances> Environment<C> for Client<C> {
}
async fn free_native_balance(&mut self, account: C::AccountId) -> Result<C::Balance, String> {
Client::<C>::free_native_balance(self, account)
.await
.map_err(|e| e.to_string())
Client::<C>::free_native_balance(self, account).await.map_err(|e| e.to_string())
}
}
@@ -196,8 +198,9 @@ mod tests {
const STORAGE_PROOF_OVERHEAD: u32 = 0;
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 0;
type SignedBlock =
sp_runtime::generic::SignedBlock<sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>>;
type SignedBlock = sp_runtime::generic::SignedBlock<
sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>,
>;
type Call = ();
type WeightToFee = IdentityFee<u32>;
}
@@ -257,10 +260,7 @@ mod tests {
// client responds with wrong version
runtime_version_tx
.send(RuntimeVersion {
spec_version: 42,
..Default::default()
})
.send(RuntimeVersion { spec_version: 42, ..Default::default() })
.await
.unwrap();
@@ -292,10 +292,7 @@ mod tests {
// client responds with the same version
runtime_version_tx
.send(RuntimeVersion {
spec_version: 42,
..Default::default()
})
.send(RuntimeVersion { spec_version: 42, ..Default::default() })
.await
.unwrap();
@@ -16,9 +16,11 @@
//! Default generic implementation of headers source for basic Substrate client.
use crate::chain::{BlockWithJustification, Chain};
use crate::client::Client;
use crate::error::Error;
use crate::{
chain::{BlockWithJustification, Chain},
client::Client,
error::Error,
};
use async_trait::async_trait;
use headers_relay::{
@@ -38,19 +40,13 @@ pub struct HeadersSource<C: Chain, 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(),
}
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(),
}
HeadersSource { client: self.client.clone(), _phantom: Default::default() }
}
}
@@ -69,7 +65,12 @@ where
C: Chain,
C::BlockNumber: relay_utils::BlockNumberBase,
C::Header: Into<P::Header>,
P: HeadersSyncPipeline<Extra = (), Completion = EncodedJustification, Hash = C::Hash, Number = C::BlockNumber>,
P: HeadersSyncPipeline<
Extra = (),
Completion = EncodedJustification,
Hash = C::Hash,
Number = C::BlockNumber,
>,
P::Header: SourceHeader<C::Hash, C::BlockNumber>,
{
async fn best_block_number(&self) -> Result<P::Number, Error> {
@@ -79,22 +80,17 @@ where
}
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)
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)
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> {
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();
@@ -102,7 +98,11 @@ where
Ok((id, grandpa_justification))
}
async fn header_extra(&self, id: HeaderIdOf<P>, _header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, ()), Error> {
async fn header_extra(
&self,
id: HeaderIdOf<P>,
_header: QueuedHeader<P>,
) -> Result<(HeaderIdOf<P>, ()), Error> {
Ok((id, ()))
}
}
+17 -15
View File
@@ -31,16 +31,18 @@ pub mod metrics;
use std::time::Duration;
pub use crate::chain::{
BlockWithJustification, CallOf, Chain, ChainWithBalances, TransactionSignScheme, TransactionStatusOf,
UnsignedTransaction, WeightToFeeOf,
pub use crate::{
chain::{
BlockWithJustification, CallOf, Chain, ChainWithBalances, TransactionSignScheme,
TransactionStatusOf, UnsignedTransaction, WeightToFeeOf,
},
client::{Client, OpaqueGrandpaAuthoritiesSet, Subscription},
error::{Error, Result},
sync_header::SyncHeader,
};
pub use crate::client::{Client, OpaqueGrandpaAuthoritiesSet, Subscription};
pub use crate::error::{Error, Result};
pub use crate::sync_header::SyncHeader;
pub use bp_runtime::{
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, IndexOf, SignatureOf,
TransactionEra, TransactionEraOf,
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf,
IndexOf, SignatureOf, TransactionEra, TransactionEraOf,
};
/// Header id used by the chain.
@@ -59,11 +61,7 @@ pub struct ConnectionParams {
impl Default for ConnectionParams {
fn default() -> Self {
ConnectionParams {
host: "localhost".into(),
port: 9944,
secure: false,
}
ConnectionParams { host: "localhost".into(), port: 9944, secure: false }
}
}
@@ -73,7 +71,11 @@ impl Default for ConnectionParams {
/// been mined for this period.
///
/// Returns `None` if mortality period is `None`
pub fn transaction_stall_timeout(mortality_period: Option<u32>, average_block_interval: Duration) -> Option<Duration> {
pub fn transaction_stall_timeout(
mortality_period: Option<u32>,
average_block_interval: Duration,
) -> Option<Duration> {
// 1 extra block for transaction to reach the pool && 1 for relayer to awake after it is mined
mortality_period.map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1))
mortality_period
.map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1))
}
@@ -14,8 +14,7 @@
// 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::chain::Chain;
use crate::client::Client;
use crate::{chain::Chain, client::Client};
use async_std::sync::{Arc, RwLock};
use async_trait::async_trait;
@@ -83,7 +82,8 @@ where
.await
.map(|maybe_storage_value| {
maybe_storage_value.or(self.maybe_default_value).map(|storage_value| {
storage_value.into_inner().unique_saturated_into() as f64 / T::DIV.unique_saturated_into() as f64
storage_value.into_inner().unique_saturated_into() as f64 /
T::DIV.unique_saturated_into() as f64
})
})
.map_err(drop);
@@ -14,12 +14,12 @@
// 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::chain::Chain;
use crate::client::Client;
use crate::error::Error;
use crate::{chain::Chain, client::Client, error::Error};
use async_trait::async_trait;
use relay_utils::metrics::{metric_name, register, Gauge, PrometheusError, Registry, StandaloneMetrics, U64};
use relay_utils::metrics::{
metric_name, register, Gauge, PrometheusError, Registry, StandaloneMetrics, U64,
};
use sp_core::storage::StorageKey;
use sp_runtime::traits::Header as HeaderT;
use sp_storage::well_known_keys::CODE;
@@ -40,10 +40,7 @@ pub struct StorageProofOverheadMetric<C: Chain> {
impl<C: Chain> Clone for StorageProofOverheadMetric<C> {
fn clone(&self) -> Self {
StorageProofOverheadMetric {
client: self.client.clone(),
metric: self.metric.clone(),
}
StorageProofOverheadMetric { client: self.client.clone(), metric: self.metric.clone() }
}
}
@@ -73,15 +70,15 @@ impl<C: Chain> StorageProofOverheadMetric<C> {
.await?;
let storage_proof_size: usize = storage_proof.clone().iter_nodes().map(|n| n.len()).sum();
let storage_value_reader =
bp_runtime::StorageProofChecker::<C::Hasher>::new(*best_header.state_root(), storage_proof)
.map_err(Error::StorageProofError)?;
let maybe_encoded_storage_value = storage_value_reader
.read_value(CODE)
.map_err(Error::StorageProofError)?;
let encoded_storage_value_size = maybe_encoded_storage_value
.ok_or(Error::MissingMandatoryCodeEntry)?
.len();
let storage_value_reader = bp_runtime::StorageProofChecker::<C::Hasher>::new(
*best_header.state_root(),
storage_proof,
)
.map_err(Error::StorageProofError)?;
let maybe_encoded_storage_value =
storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?;
let encoded_storage_value_size =
maybe_encoded_storage_value.ok_or(Error::MissingMandatoryCodeEntry)?.len();
Ok(storage_proof_size - encoded_storage_value_size)
}
+13 -8
View File
@@ -18,7 +18,8 @@
use codec::Encode;
use relay_substrate_client::{
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme,
UnsignedTransaction,
};
use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
@@ -78,7 +79,13 @@ impl TransactionSignScheme for Wococo {
) -> Self::SignedTransaction {
let raw_payload = SignedPayload::new(
unsigned.call,
bp_wococo::SignedExtensions::new(bp_wococo::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
bp_wococo::SignedExtensions::new(
bp_wococo::VERSION,
era,
genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
@@ -101,17 +108,15 @@ impl TransactionSignScheme for Wococo {
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_wococo::AccountId::from(*signer.public().as_array_ref()).into())
.map(|(address, _, _)| {
*address == bp_wococo::AccountId::from(*signer.public().as_array_ref()).into()
})
.unwrap_or(false)
}
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
let extra = &tx.signature.as_ref()?.2;
Some(UnsignedTransaction {
call: tx.function,
nonce: extra.nonce(),
tip: extra.tip(),
})
Some(UnsignedTransaction { call: tx.function, nonce: extra.nonce(), tip: extra.tip() })
}
}
+3 -1
View File
@@ -115,7 +115,9 @@ pub enum BridgeMessagesRococoCall {
),
#[codec(index = 6)]
receive_messages_delivery_proof(
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_rococo::Hash>,
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<
bp_rococo::Hash,
>,
UnrewardedRelayersState,
),
}
+129 -126
View File
@@ -18,7 +18,8 @@
use async_trait::async_trait;
use relay_utils::{
relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, StringifiedMaybeConnectionError,
relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError,
StringifiedMaybeConnectionError,
};
use std::{
fmt::{Debug, Display},
@@ -96,12 +97,18 @@ pub trait SourceClient<P: TransactionProofPipeline>: RelayClient {
async fn block_by_hash(&self, hash: BlockHashOf<P>) -> Result<P::Block, Self::Error>;
/// Get canonical block by number.
async fn block_by_number(&self, number: BlockNumberOf<P>) -> Result<P::Block, Self::Error>;
/// Return block + index where transaction has been **mined**. May return `Ok(None)` if transaction
/// is unknown to the source node.
async fn transaction_block(&self, hash: &TransactionHashOf<P>)
-> Result<Option<(HeaderId<P>, usize)>, Self::Error>;
/// Return block + index where transaction has been **mined**. May return `Ok(None)` if
/// transaction is unknown to the source node.
async fn transaction_block(
&self,
hash: &TransactionHashOf<P>,
) -> Result<Option<(HeaderId<P>, usize)>, Self::Error>;
/// Prepare transaction proof.
async fn transaction_proof(&self, block: &P::Block, tx_index: usize) -> Result<P::TransactionProof, Self::Error>;
async fn transaction_proof(
&self,
block: &P::Block,
tx_index: usize,
) -> Result<P::TransactionProof, Self::Error>;
}
/// Target client API.
@@ -116,9 +123,13 @@ pub trait TargetClient<P: TransactionProofPipeline>: RelayClient {
/// Returns best finalized header id.
async fn best_finalized_header_id(&self) -> Result<HeaderId<P>, Self::Error>;
/// Returns `Ok(true)` if transaction proof is need to be relayed.
async fn filter_transaction_proof(&self, proof: &P::TransactionProof) -> Result<bool, Self::Error>;
async fn filter_transaction_proof(
&self,
proof: &P::TransactionProof,
) -> Result<bool, Self::Error>;
/// Submits transaction proof to the target node.
async fn submit_transaction_proof(&self, proof: P::TransactionProof) -> Result<(), Self::Error>;
async fn submit_transaction_proof(&self, proof: P::TransactionProof)
-> Result<(), Self::Error>;
}
/// Block transaction statistics.
@@ -154,27 +165,28 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
for (source_tx_index, source_tx) in transactions_to_process {
let result = async {
let source_tx_id = format!("{}/{}", source_block.id().1, source_tx_index);
let source_tx_proof =
prepare_transaction_proof(source_client, &source_tx_id, source_block, source_tx_index)
.await
.map_err(|e| (FailedClient::Source, e))?;
let source_tx_proof = prepare_transaction_proof(
source_client,
&source_tx_id,
source_block,
source_tx_index,
)
.await
.map_err(|e| (FailedClient::Source, e))?;
let needs_to_be_relayed =
target_client
.filter_transaction_proof(&source_tx_proof)
.await
.map_err(|err| {
(
FailedClient::Target,
StringifiedMaybeConnectionError::new(
err.is_connection_error(),
format!("Transaction filtering has failed with {:?}", err),
),
)
})?;
target_client.filter_transaction_proof(&source_tx_proof).await.map_err(|err| {
(
FailedClient::Target,
StringifiedMaybeConnectionError::new(
err.is_connection_error(),
format!("Transaction filtering has failed with {:?}", err),
),
)
})?;
if !needs_to_be_relayed {
return Ok(false);
return Ok(false)
}
relay_ready_transaction_proof(target_client, &source_tx_id, source_tx_proof)
@@ -191,13 +203,14 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
// Option#1 may seems better, but:
// 1) we do not track if transaction is mined (without an error) by the target node;
// 2) error could be irrecoverable (e.g. when block is already pruned by bridge module or tx
// has invalid format) && we'll end up in infinite loop of retrying the same transaction proof.
// has invalid format) && we'll end up in infinite loop of retrying the same transaction
// proof.
//
// So we're going with option#2 here (the only exception are connection errors).
match result {
Ok(false) => {
relayed_transactions.processed += 1;
}
},
Ok(true) => {
log::info!(
target: "bridge",
@@ -209,7 +222,7 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
relayed_transactions.processed += 1;
relayed_transactions.relayed += 1;
}
},
Err((failed_client, err)) => {
log::error!(
target: "bridge",
@@ -226,12 +239,12 @@ pub async fn relay_block_transactions<P: TransactionProofPipeline>(
);
if err.is_connection_error() {
return Err((failed_client, relayed_transactions));
return Err((failed_client, relayed_transactions))
}
relayed_transactions.processed += 1;
relayed_transactions.failed += 1;
}
},
}
}
@@ -245,7 +258,8 @@ pub async fn relay_single_transaction_proof<P: TransactionProofPipeline>(
source_tx_hash: TransactionHashOf<P>,
) -> Result<(), String> {
// wait for transaction and header on source node
let (source_header_id, source_tx_index) = wait_transaction_mined(source_client, &source_tx_hash).await?;
let (source_header_id, source_tx_index) =
wait_transaction_mined(source_client, &source_tx_hash).await?;
let source_block = source_client.block_by_hash(source_header_id.1.clone()).await;
let source_block = source_block.map_err(|err| {
format!(
@@ -302,20 +316,17 @@ async fn relay_ready_transaction_proof<P: TransactionProofPipeline>(
source_tx_id: &str,
source_tx_proof: P::TransactionProof,
) -> Result<(), StringifiedMaybeConnectionError> {
target_client
.submit_transaction_proof(source_tx_proof)
.await
.map_err(|err| {
StringifiedMaybeConnectionError::new(
err.is_connection_error(),
format!(
"Error submitting transaction {} proof to {} node: {:?}",
source_tx_id,
P::TARGET_NAME,
err,
),
)
})
target_client.submit_transaction_proof(source_tx_proof).await.map_err(|err| {
StringifiedMaybeConnectionError::new(
err.is_connection_error(),
format!(
"Error submitting transaction {} proof to {} node: {:?}",
source_tx_id,
P::TARGET_NAME,
err,
),
)
})
}
/// Wait until transaction is mined by source node.
@@ -324,14 +335,15 @@ async fn wait_transaction_mined<P: TransactionProofPipeline>(
source_tx_hash: &TransactionHashOf<P>,
) -> Result<(HeaderId<P>, usize), String> {
loop {
let source_header_and_tx = source_client.transaction_block(source_tx_hash).await.map_err(|err| {
format!(
"Error retrieving transaction {} from {} node: {:?}",
source_tx_hash,
P::SOURCE_NAME,
err,
)
})?;
let source_header_and_tx =
source_client.transaction_block(source_tx_hash).await.map_err(|err| {
format!(
"Error retrieving transaction {} from {} node: {:?}",
source_tx_hash,
P::SOURCE_NAME,
err,
)
})?;
match source_header_and_tx {
Some((source_header_id, source_tx)) => {
log::info!(
@@ -341,8 +353,8 @@ async fn wait_transaction_mined<P: TransactionProofPipeline>(
P::SOURCE_NAME,
);
return Ok((source_header_id, source_tx));
}
return Ok((source_header_id, source_tx))
},
None => {
log::info!(
target: "bridge",
@@ -352,7 +364,7 @@ async fn wait_transaction_mined<P: TransactionProofPipeline>(
);
source_client.tick().await;
}
},
}
}
}
@@ -363,15 +375,16 @@ async fn wait_header_imported<P: TransactionProofPipeline>(
source_header_id: &HeaderId<P>,
) -> Result<(), String> {
loop {
let is_header_known = target_client.is_header_known(source_header_id).await.map_err(|err| {
format!(
"Failed to check existence of header {}/{} on {} node: {:?}",
source_header_id.0,
source_header_id.1,
P::TARGET_NAME,
err,
)
})?;
let is_header_known =
target_client.is_header_known(source_header_id).await.map_err(|err| {
format!(
"Failed to check existence of header {}/{} on {} node: {:?}",
source_header_id.0,
source_header_id.1,
P::TARGET_NAME,
err,
)
})?;
match is_header_known {
true => {
log::info!(
@@ -382,8 +395,8 @@ async fn wait_header_imported<P: TransactionProofPipeline>(
P::TARGET_NAME,
);
return Ok(());
}
return Ok(())
},
false => {
log::info!(
target: "bridge",
@@ -394,7 +407,7 @@ async fn wait_header_imported<P: TransactionProofPipeline>(
);
target_client.tick().await;
}
},
}
}
}
@@ -405,10 +418,8 @@ async fn wait_header_finalized<P: TransactionProofPipeline>(
source_header_id: &HeaderId<P>,
) -> Result<(), String> {
loop {
let is_header_finalized = target_client
.is_header_finalized(source_header_id)
.await
.map_err(|err| {
let is_header_finalized =
target_client.is_header_finalized(source_header_id).await.map_err(|err| {
format!(
"Failed to check finality of header {}/{} on {} node: {:?}",
source_header_id.0,
@@ -427,8 +438,8 @@ async fn wait_header_finalized<P: TransactionProofPipeline>(
P::TARGET_NAME,
);
return Ok(());
}
return Ok(())
},
false => {
log::info!(
target: "bridge",
@@ -439,7 +450,7 @@ async fn wait_header_finalized<P: TransactionProofPipeline>(
);
target_client.tick().await;
}
},
}
}
}
@@ -582,15 +593,22 @@ pub(crate) mod tests {
self.data.lock().block.clone()
}
async fn transaction_block(&self, _: &TestTransactionHash) -> Result<Option<(TestHeaderId, usize)>, TestError> {
async fn transaction_block(
&self,
_: &TestTransactionHash,
) -> Result<Option<(TestHeaderId, usize)>, TestError> {
self.data.lock().transaction_block.clone()
}
async fn transaction_proof(&self, block: &TestBlock, index: usize) -> Result<TestTransactionProof, TestError> {
async fn transaction_proof(
&self,
block: &TestBlock,
index: usize,
) -> Result<TestTransactionProof, TestError> {
let tx_hash = block.1[index].hash();
let proof_error = self.data.lock().proofs_to_fail.get(&tx_hash).cloned();
if let Some(err) = proof_error {
return Err(err);
return Err(err)
}
Ok(TestTransactionProof(tx_hash))
@@ -653,19 +671,32 @@ pub(crate) mod tests {
self.data.lock().best_finalized_header_id.clone()
}
async fn filter_transaction_proof(&self, proof: &TestTransactionProof) -> Result<bool, TestError> {
async fn filter_transaction_proof(
&self,
proof: &TestTransactionProof,
) -> Result<bool, TestError> {
Ok(self.data.lock().transactions_to_accept.contains(&proof.0))
}
async fn submit_transaction_proof(&self, proof: TestTransactionProof) -> Result<(), TestError> {
async fn submit_transaction_proof(
&self,
proof: TestTransactionProof,
) -> Result<(), TestError> {
self.data.lock().submitted_proofs.push(proof);
Ok(())
}
}
fn ensure_relay_single_success(source: &TestTransactionsSource, target: &TestTransactionsTarget) {
fn ensure_relay_single_success(
source: &TestTransactionsSource,
target: &TestTransactionsTarget,
) {
assert_eq!(
async_std::task::block_on(relay_single_transaction_proof(source, target, test_transaction_hash(0),)),
async_std::task::block_on(relay_single_transaction_proof(
source,
target,
test_transaction_hash(0),
)),
Ok(()),
);
assert_eq!(
@@ -782,11 +813,7 @@ pub(crate) mod tests {
let source = TestTransactionsSource::new(Box::new(|_| unreachable!("no ticks allowed")));
let target = TestTransactionsTarget::new(Box::new(|_| unreachable!("no ticks allowed")));
target
.data
.lock()
.transactions_to_accept
.remove(&test_transaction_hash(0));
target.data.lock().transactions_to_accept.remove(&test_transaction_hash(0));
ensure_relay_single_success(&source, &target)
}
@@ -814,25 +841,14 @@ pub(crate) mod tests {
let target = TestTransactionsTarget::new(Box::new(|_| unreachable!("no ticks allowed")));
// let's only accept tx#1
target
.data
.lock()
.transactions_to_accept
.remove(&test_transaction_hash(0));
target
.data
.lock()
.transactions_to_accept
.insert(test_transaction_hash(1));
target.data.lock().transactions_to_accept.remove(&test_transaction_hash(0));
target.data.lock().transactions_to_accept.insert(test_transaction_hash(1));
let relayed_transactions = test_relay_block_transactions(&source, &target, Default::default());
let relayed_transactions =
test_relay_block_transactions(&source, &target, Default::default());
assert_eq!(
relayed_transactions,
Ok(RelayedBlockTransactions {
processed: 3,
relayed: 1,
failed: 0,
}),
Ok(RelayedBlockTransactions { processed: 3, relayed: 1, failed: 0 }),
);
assert_eq!(
target.data.lock().submitted_proofs,
@@ -852,14 +868,11 @@ pub(crate) mod tests {
.proofs_to_fail
.insert(test_transaction_hash(0), TestError(false));
let relayed_transactions = test_relay_block_transactions(&source, &target, Default::default());
let relayed_transactions =
test_relay_block_transactions(&source, &target, Default::default());
assert_eq!(
relayed_transactions,
Ok(RelayedBlockTransactions {
processed: 3,
relayed: 0,
failed: 1,
}),
Ok(RelayedBlockTransactions { processed: 3, relayed: 0, failed: 1 }),
);
assert_eq!(target.data.lock().submitted_proofs, vec![],);
}
@@ -876,14 +889,11 @@ pub(crate) mod tests {
.proofs_to_fail
.insert(test_transaction_hash(1), TestError(true));
let relayed_transactions = test_relay_block_transactions(&source, &target, Default::default());
let relayed_transactions =
test_relay_block_transactions(&source, &target, Default::default());
assert_eq!(
relayed_transactions,
Err(RelayedBlockTransactions {
processed: 1,
relayed: 1,
failed: 0,
}),
Err(RelayedBlockTransactions { processed: 1, relayed: 1, failed: 0 }),
);
assert_eq!(
target.data.lock().submitted_proofs,
@@ -893,20 +903,13 @@ pub(crate) mod tests {
// now do not fail on tx#2
source.data.lock().proofs_to_fail.clear();
// and also relay tx#3
target
.data
.lock()
.transactions_to_accept
.insert(test_transaction_hash(2));
target.data.lock().transactions_to_accept.insert(test_transaction_hash(2));
let relayed_transactions = test_relay_block_transactions(&source, &target, relayed_transactions.unwrap_err());
let relayed_transactions =
test_relay_block_transactions(&source, &target, relayed_transactions.unwrap_err());
assert_eq!(
relayed_transactions,
Ok(RelayedBlockTransactions {
processed: 3,
relayed: 2,
failed: 0,
}),
Ok(RelayedBlockTransactions { processed: 3, relayed: 2, failed: 0 }),
);
assert_eq!(
target.data.lock().submitted_proofs,
+40 -36
View File
@@ -16,11 +16,13 @@
//! Relaying proofs of exchange transactions.
use crate::exchange::{
relay_block_transactions, BlockNumberOf, RelayedBlockTransactions, SourceClient, TargetClient,
TransactionProofPipeline,
use crate::{
exchange::{
relay_block_transactions, BlockNumberOf, RelayedBlockTransactions, SourceClient,
TargetClient, TransactionProofPipeline,
},
exchange_loop_metrics::ExchangeLoopMetrics,
};
use crate::exchange_loop_metrics::ExchangeLoopMetrics;
use backoff::backoff::Backoff;
use futures::{future::FutureExt, select};
@@ -58,13 +60,13 @@ pub struct InMemoryStorage<BlockNumber> {
impl<BlockNumber> InMemoryStorage<BlockNumber> {
/// Created new in-memory storage with given best processed block number.
pub fn new(best_processed_header_number: BlockNumber) -> Self {
InMemoryStorage {
best_processed_header_number,
}
InMemoryStorage { best_processed_header_number }
}
}
impl<BlockNumber: 'static + Clone + Copy + Send + Sync> TransactionProofsRelayStorage for InMemoryStorage<BlockNumber> {
impl<BlockNumber: 'static + Clone + Copy + Send + Sync> TransactionProofsRelayStorage
for InMemoryStorage<BlockNumber>
{
type BlockNumber = BlockNumber;
fn state(&self) -> TransactionProofsRelayState<BlockNumber> {
@@ -140,12 +142,11 @@ async fn run_until_connection_lost<P: TransactionProofPipeline>(
if let Err((is_connection_error, failed_client)) = iteration_result {
if is_connection_error {
return Err(failed_client);
return Err(failed_client)
}
let retry_timeout = retry_backoff
.next_backoff()
.unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY);
let retry_timeout =
retry_backoff.next_backoff().unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY);
select! {
_ = async_std::task::sleep(retry_timeout).fuse() => {},
_ = exit_signal => return Ok(()),
@@ -181,7 +182,7 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
);
best_finalized_header_id
}
},
Err(err) => {
log::error!(
target: "bridge",
@@ -191,14 +192,20 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
err,
);
return Err((err.is_connection_error(), FailedClient::Target));
}
return Err((err.is_connection_error(), FailedClient::Target))
},
};
loop {
// if we already have some finalized block body, try to relay its transactions
if let Some((block, relayed_transactions)) = current_finalized_block.take() {
let result = relay_block_transactions(source_client, target_client, &block, relayed_transactions).await;
let result = relay_block_transactions(
source_client,
target_client,
&block,
relayed_transactions,
)
.await;
match result {
Ok(relayed_transactions) => {
@@ -212,7 +219,8 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
relayed_transactions.failed,
);
state.best_processed_header_number = state.best_processed_header_number + One::one();
state.best_processed_header_number =
state.best_processed_header_number + One::one();
storage.set_state(state);
if let Some(exchange_loop_metrics) = exchange_loop_metrics {
@@ -224,11 +232,11 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
}
// we have just updated state => proceed to next block retrieval
}
},
Err((failed_client, relayed_transactions)) => {
*current_finalized_block = Some((block, relayed_transactions));
return Err((true, failed_client));
}
return Err((true, failed_client))
},
}
}
@@ -242,8 +250,8 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
*current_finalized_block = Some((block, RelayedBlockTransactions::default()));
// we have received new finalized block => go back to relay its transactions
continue;
}
continue
},
Err(err) => {
log::error!(
target: "bridge",
@@ -253,13 +261,13 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
err,
);
return Err((err.is_connection_error(), FailedClient::Source));
}
return Err((err.is_connection_error(), FailedClient::Source))
},
}
}
// there are no any transactions we need to relay => wait for new data
return Ok(());
return Ok(())
}
}
@@ -267,17 +275,16 @@ async fn run_loop_iteration<P: TransactionProofPipeline>(
mod tests {
use super::*;
use crate::exchange::tests::{
test_next_block, test_next_block_id, test_transaction_hash, TestTransactionProof, TestTransactionsSource,
TestTransactionsTarget,
test_next_block, test_next_block_id, test_transaction_hash, TestTransactionProof,
TestTransactionsSource, TestTransactionsTarget,
};
use futures::{future::FutureExt, stream::StreamExt};
#[test]
fn exchange_loop_is_able_to_relay_proofs() {
let storage = InMemoryStorage {
best_processed_header_number: 0,
};
let target = TestTransactionsTarget::new(Box::new(|_| unreachable!("no target ticks allowed")));
let storage = InMemoryStorage { best_processed_header_number: 0 };
let target =
TestTransactionsTarget::new(Box::new(|_| unreachable!("no target ticks allowed")));
let target_data = target.data.clone();
let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded();
@@ -295,11 +302,8 @@ mod tests {
(true, false) => {
data.block = Ok(test_next_block());
target_data.lock().best_finalized_header_id = Ok(test_next_block_id());
target_data
.lock()
.transactions_to_accept
.insert(test_transaction_hash(1));
}
target_data.lock().transactions_to_accept.insert(test_transaction_hash(1));
},
_ => (),
}
}));
+67 -70
View File
@@ -43,18 +43,19 @@ pub struct FinalitySyncParams {
/// `min(source_block_time, target_block_time)`.
///
/// This parameter may be used to limit transactions rate. Increase the value && you'll get
/// infrequent updates => sparse headers => potential slow down of bridge applications, but pallet storage
/// won't be super large. Decrease the value to near `source_block_time` and you'll get
/// transaction for (almost) every block of the source chain => all source headers will be known
/// to the target chain => bridge applications will run faster, but pallet storage may explode
/// (but if pruning is there, then it's fine).
/// infrequent updates => sparse headers => potential slow down of bridge applications, but
/// pallet storage won't be super large. Decrease the value to near `source_block_time` and
/// you'll get transaction for (almost) every block of the source chain => all source headers
/// will be known to the target chain => bridge applications will run faster, but pallet
/// storage may explode (but if pruning is there, then it's fine).
pub tick: Duration,
/// Number of finality proofs to keep in internal buffer between loop iterations.
///
/// While in "major syncing" state, we still read finality proofs from the stream. They're stored
/// in the internal buffer between loop iterations. When we're close to the tip of the chain, we may
/// meet finality delays if headers are not finalized frequently. So instead of waiting for next
/// finality proof to appear in the stream, we may use existing proof from that buffer.
/// While in "major syncing" state, we still read finality proofs from the stream. They're
/// stored in the internal buffer between loop iterations. When we're close to the tip of the
/// chain, we may meet finality delays if headers are not finalized frequently. So instead of
/// waiting for next finality proof to appear in the stream, we may use existing proof from
/// that buffer.
pub recent_finality_proofs_limit: usize,
/// Timeout before we treat our transactions as lost and restart the whole sync process.
pub stall_timeout: Duration,
@@ -89,10 +90,15 @@ pub trait TargetClient<P: FinalitySyncPipeline>: RelayClient {
async fn best_finalized_source_block_number(&self) -> Result<P::Number, Self::Error>;
/// Submit header finality proof.
async fn submit_finality_proof(&self, header: P::Header, proof: P::FinalityProof) -> Result<(), Self::Error>;
async fn submit_finality_proof(
&self,
header: P::Header,
proof: P::FinalityProof,
) -> Result<(), Self::Error>;
}
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs sync loop.
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
/// sync loop.
pub fn metrics_prefix<P: FinalitySyncPipeline>() -> String {
format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME)
}
@@ -127,15 +133,11 @@ pub async fn run<P: FinalitySyncPipeline>(
/// Unjustified headers container. Ordered by header number.
pub(crate) type UnjustifiedHeaders<H> = Vec<H>;
/// Finality proofs container. Ordered by target header number.
pub(crate) type FinalityProofs<P> = Vec<(
<P as FinalitySyncPipeline>::Number,
<P as FinalitySyncPipeline>::FinalityProof,
)>;
pub(crate) type FinalityProofs<P> =
Vec<(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)>;
/// Reference to finality proofs container.
pub(crate) type FinalityProofsRef<'a, P> = &'a [(
<P as FinalitySyncPipeline>::Number,
<P as FinalitySyncPipeline>::FinalityProof,
)];
pub(crate) type FinalityProofsRef<'a, P> =
&'a [(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)];
/// Error that may happen inside finality synchronization loop.
#[derive(Debug)]
@@ -186,10 +188,7 @@ pub(crate) struct RestartableFinalityProofsStream<S> {
#[cfg(test)]
impl<S> From<S> for RestartableFinalityProofsStream<S> {
fn from(stream: S) -> Self {
RestartableFinalityProofsStream {
needs_restart: false,
stream: Box::pin(stream),
}
RestartableFinalityProofsStream { needs_restart: false, stream: Box::pin(stream) }
}
}
@@ -260,14 +259,12 @@ async fn run_until_connection_lost<P: FinalitySyncPipeline>(
last_transaction = updated_last_transaction;
retry_backoff.reset();
sync_params.tick
}
},
Err(error) => {
log::error!(target: "bridge", "Finality sync loop iteration has failed with error: {:?}", error);
error.fail_if_connection_error()?;
retry_backoff
.next_backoff()
.unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY)
}
retry_backoff.next_backoff().unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY)
},
};
if finality_proofs_stream.needs_restart {
log::warn!(target: "bridge", "{} finality proofs stream is being restarted", P::SOURCE_NAME);
@@ -297,10 +294,8 @@ where
TC: TargetClient<P>,
{
// read best source headers ids from source and target nodes
let best_number_at_source = source_client
.best_finalized_block_number()
.await
.map_err(Error::Source)?;
let best_number_at_source =
source_client.best_finalized_block_number().await.map_err(Error::Source)?;
let best_number_at_target = target_client
.best_finalized_source_block_number()
.await
@@ -309,7 +304,8 @@ where
metrics_sync.update_best_block_at_source(best_number_at_source);
metrics_sync.update_best_block_at_target(best_number_at_target);
}
*state.progress = print_sync_progress::<P>(*state.progress, best_number_at_source, best_number_at_target);
*state.progress =
print_sync_progress::<P>(*state.progress, best_number_at_source, best_number_at_target);
// if we have already submitted header, then we just need to wait for it
// if we're waiting too much, then we believe our transaction has been lost and restart sync
@@ -324,9 +320,9 @@ where
P::TARGET_NAME,
);
return Err(Error::Stalled);
return Err(Error::Stalled)
} else {
return Ok(Some(last_transaction));
return Ok(Some(last_transaction))
}
}
@@ -343,10 +339,8 @@ where
.await?
{
Some((header, justification)) => {
let new_transaction = Transaction {
time: Instant::now(),
submitted_header_number: header.number(),
};
let new_transaction =
Transaction { time: Instant::now(), submitted_header_number: header.number() };
log::debug!(
target: "bridge",
@@ -361,7 +355,7 @@ where
.await
.map_err(Error::Target)?;
Ok(Some(new_transaction))
}
},
None => Ok(None),
}
}
@@ -398,15 +392,15 @@ where
)
.await?;
let (mut unjustified_headers, mut selected_finality_proof) = match selected_finality_proof {
SelectedFinalityProof::Mandatory(header, finality_proof) => return Ok(Some((header, finality_proof))),
SelectedFinalityProof::Mandatory(header, finality_proof) =>
return Ok(Some((header, finality_proof))),
_ if sync_params.only_mandatory_headers => {
// we are not reading finality proofs from the stream, so eventually it'll break
// but we don't care about transient proofs at all, so it is acceptable
return Ok(None);
}
SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) => {
(unjustified_headers, Some((header, finality_proof)))
}
return Ok(None)
},
SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) =>
(unjustified_headers, Some((header, finality_proof))),
SelectedFinalityProof::None(unjustified_headers) => (unjustified_headers, None),
};
@@ -451,7 +445,11 @@ pub(crate) enum SelectedFinalityProof<Header, FinalityProof> {
/// Otherwise, `SelectedFinalityProof::None` is returned.
///
/// Unless we have found mandatory header, all missing headers are collected and returned.
pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClient<P>, TC: TargetClient<P>>(
pub(crate) async fn read_missing_headers<
P: FinalitySyncPipeline,
SC: SourceClient<P>,
TC: TargetClient<P>,
>(
source_client: &SC,
_target_client: &TC,
best_number_at_source: P::Number,
@@ -470,17 +468,17 @@ pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClie
match (is_mandatory, finality_proof) {
(true, Some(finality_proof)) => {
log::trace!(target: "bridge", "Header {:?} is mandatory", header_number);
return Ok(SelectedFinalityProof::Mandatory(header, finality_proof));
}
return Ok(SelectedFinalityProof::Mandatory(header, finality_proof))
},
(true, None) => return Err(Error::MissingMandatoryFinalityProof(header.number())),
(false, Some(finality_proof)) => {
log::trace!(target: "bridge", "Header {:?} has persistent finality proof", header_number);
unjustified_headers.clear();
selected_finality_proof = Some((header, finality_proof));
}
},
(false, None) => {
unjustified_headers.push(header);
}
},
}
header_number = header_number + One::one();
@@ -493,7 +491,10 @@ pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClie
}
/// Read finality proofs from the stream.
pub(crate) fn read_finality_proofs_from_stream<P: FinalitySyncPipeline, FPS: Stream<Item = P::FinalityProof>>(
pub(crate) fn read_finality_proofs_from_stream<
P: FinalitySyncPipeline,
FPS: Stream<Item = P::FinalityProof>,
>(
finality_proofs_stream: &mut RestartableFinalityProofsStream<FPS>,
recent_finality_proofs: &mut FinalityProofs<P>,
) {
@@ -506,8 +507,8 @@ pub(crate) fn read_finality_proofs_from_stream<P: FinalitySyncPipeline, FPS: Str
Some(Some(finality_proof)) => finality_proof,
Some(None) => {
finality_proofs_stream.needs_restart = true;
break;
}
break
},
None => break,
};
@@ -547,7 +548,7 @@ pub(crate) fn select_better_recent_finality_proof<P: FinalitySyncPipeline>(
P::SOURCE_NAME,
selected_finality_proof.as_ref().map(|(h, _)| h.number()),
);
return selected_finality_proof;
return selected_finality_proof
}
const NOT_EMPTY_PROOF: &str = "we have checked that the vec is not empty; qed";
@@ -569,7 +570,8 @@ pub(crate) fn select_better_recent_finality_proof<P: FinalitySyncPipeline>(
let selected_finality_proof_index = recent_finality_proofs
.binary_search_by_key(intersection.end(), |(number, _)| *number)
.unwrap_or_else(|index| index.saturating_sub(1));
let (selected_header_number, finality_proof) = &recent_finality_proofs[selected_finality_proof_index];
let (selected_header_number, finality_proof) =
&recent_finality_proofs[selected_finality_proof_index];
let has_selected_finality_proof = intersection.contains(selected_header_number);
log::trace!(
target: "bridge",
@@ -585,7 +587,7 @@ pub(crate) fn select_better_recent_finality_proof<P: FinalitySyncPipeline>(
if has_selected_finality_proof { "improved" } else { "failed" },
);
if !has_selected_finality_proof {
return selected_finality_proof;
return selected_finality_proof
}
// now remove all obsolete headers and extract selected header
@@ -601,20 +603,15 @@ pub(crate) fn prune_recent_finality_proofs<P: FinalitySyncPipeline>(
recent_finality_proofs: &mut FinalityProofs<P>,
recent_finality_proofs_limit: usize,
) {
let position =
recent_finality_proofs.binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number);
let position = recent_finality_proofs
.binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number);
// remove all obsolete elements
*recent_finality_proofs = recent_finality_proofs.split_off(
position
.map(|position| position + 1)
.unwrap_or_else(|position| position),
);
*recent_finality_proofs = recent_finality_proofs
.split_off(position.map(|position| position + 1).unwrap_or_else(|position| position));
// now - limit vec by size
let split_index = recent_finality_proofs
.len()
.saturating_sub(recent_finality_proofs_limit);
let split_index = recent_finality_proofs.len().saturating_sub(recent_finality_proofs_limit);
*recent_finality_proofs = recent_finality_proofs.split_off(split_index);
}
@@ -626,15 +623,15 @@ fn print_sync_progress<P: FinalitySyncPipeline>(
let (prev_time, prev_best_number_at_target) = progress_context;
let now = Instant::now();
let need_update = now - prev_time > Duration::from_secs(10)
|| prev_best_number_at_target
let need_update = now - prev_time > Duration::from_secs(10) ||
prev_best_number_at_target
.map(|prev_best_number_at_target| {
best_number_at_target.saturating_sub(prev_best_number_at_target) > 10.into()
})
.unwrap_or(true);
if !need_update {
return (prev_time, prev_best_number_at_target);
return (prev_time, prev_best_number_at_target)
}
log::info!(
@@ -18,17 +18,21 @@
#![cfg(test)]
use crate::finality_loop::{
prune_recent_finality_proofs, read_finality_proofs_from_stream, run, select_better_recent_finality_proof,
select_header_to_submit, FinalityProofs, FinalitySyncParams, RestartableFinalityProofsStream, SourceClient,
TargetClient,
use crate::{
finality_loop::{
prune_recent_finality_proofs, read_finality_proofs_from_stream, run,
select_better_recent_finality_proof, select_header_to_submit, FinalityProofs,
FinalitySyncParams, RestartableFinalityProofsStream, SourceClient, TargetClient,
},
FinalityProof, FinalitySyncPipeline, SourceHeader,
};
use crate::{FinalityProof, FinalitySyncPipeline, SourceHeader};
use async_trait::async_trait;
use futures::{FutureExt, Stream, StreamExt};
use parking_lot::Mutex;
use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient, MaybeConnectionError};
use relay_utils::{
metrics::MetricsParams, relay_loop::Client as RelayClient, MaybeConnectionError,
};
use std::{collections::HashMap, pin::Pin, sync::Arc, time::Duration};
type IsMandatory = bool;
@@ -121,10 +125,7 @@ impl SourceClient<TestFinalitySyncPipeline> for TestSourceClient {
) -> Result<(TestSourceHeader, Option<TestFinalityProof>), TestError> {
let mut data = self.data.lock();
(self.on_method_call)(&mut *data);
data.source_headers
.get(&number)
.cloned()
.ok_or(TestError::NonConnection)
data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection)
}
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, TestError> {
@@ -157,7 +158,11 @@ impl TargetClient<TestFinalitySyncPipeline> for TestTargetClient {
Ok(data.target_best_block_number)
}
async fn submit_finality_proof(&self, header: TestSourceHeader, proof: TestFinalityProof) -> Result<(), TestError> {
async fn submit_finality_proof(
&self,
header: TestSourceHeader,
proof: TestFinalityProof,
) -> Result<(), TestError> {
let mut data = self.data.lock();
(self.on_method_call)(&mut *data);
data.target_best_block_number = header.number();
@@ -171,11 +176,12 @@ fn prepare_test_clients(
state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static,
source_headers: HashMap<TestNumber, (TestSourceHeader, Option<TestFinalityProof>)>,
) -> (TestSourceClient, TestTargetClient) {
let internal_state_function: Arc<dyn Fn(&mut ClientsData) + Send + Sync> = Arc::new(move |data| {
if state_function(data) {
exit_sender.unbounded_send(()).unwrap();
}
});
let internal_state_function: Arc<dyn Fn(&mut ClientsData) + Send + Sync> =
Arc::new(move |data| {
if state_function(data) {
exit_sender.unbounded_send(()).unwrap();
}
});
let clients_data = Arc::new(Mutex::new(ClientsData {
source_best_block_number: 10,
source_headers,
@@ -189,14 +195,13 @@ fn prepare_test_clients(
on_method_call: internal_state_function.clone(),
data: clients_data.clone(),
},
TestTargetClient {
on_method_call: internal_state_function,
data: clients_data,
},
TestTargetClient { on_method_call: internal_state_function, data: clients_data },
)
}
fn run_sync_loop(state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static) -> ClientsData {
fn run_sync_loop(
state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static,
) -> ClientsData {
let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded();
let (source_client, target_client) = prepare_test_clients(
exit_sender,
@@ -234,12 +239,13 @@ fn run_sync_loop(state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync
#[test]
fn finality_sync_loop_works() {
let client_data = run_sync_loop(|data| {
// header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted, because
// header#8 has persistent finality proof && it is mandatory => it is submitted
// header#9 has persistent finality proof, but it isn't mandatory => it is submitted, because
// there are no more persistent finality proofs
// header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted,
// because header#8 has persistent finality proof && it is mandatory => it is submitted
// header#9 has persistent finality proof, but it isn't mandatory => it is submitted,
// because there are no more persistent finality proofs
//
// once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from the stream
// once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from
// the stream
if data.target_best_block_number == 9 {
data.source_best_block_number = 14;
data.source_headers.insert(11, (TestSourceHeader(false, 11), None));
@@ -287,10 +293,7 @@ fn run_only_mandatory_headers_mode_test(
vec![
(6, (TestSourceHeader(false, 6), Some(TestFinalityProof(6)))),
(7, (TestSourceHeader(false, 7), Some(TestFinalityProof(7)))),
(
8,
(TestSourceHeader(has_mandatory_headers, 8), Some(TestFinalityProof(8))),
),
(8, (TestSourceHeader(has_mandatory_headers, 8), Some(TestFinalityProof(8)))),
(9, (TestSourceHeader(false, 9), Some(TestFinalityProof(9)))),
(10, (TestSourceHeader(false, 10), Some(TestFinalityProof(10)))),
]
@@ -357,7 +360,8 @@ fn select_better_recent_finality_proof_works() {
Some((TestSourceHeader(false, 2), TestFinalityProof(2))),
);
// if there's no intersection between recent finality proofs and unjustified headers, nothing is changed
// if there's no intersection between recent finality proofs and unjustified headers, nothing is
// changed
let mut unjustified_headers = vec![TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
assert_eq!(
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
@@ -368,13 +372,10 @@ fn select_better_recent_finality_proof_works() {
Some((TestSourceHeader(false, 2), TestFinalityProof(2))),
);
// if there's intersection between recent finality proofs and unjustified headers, but there are no
// proofs in this intersection, nothing is changed
let mut unjustified_headers = vec![
TestSourceHeader(false, 8),
TestSourceHeader(false, 9),
TestSourceHeader(false, 10),
];
// if there's intersection between recent finality proofs and unjustified headers, but there are
// no proofs in this intersection, nothing is changed
let mut unjustified_headers =
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
assert_eq!(
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
&[(7, TestFinalityProof(7)), (11, TestFinalityProof(11))],
@@ -385,22 +386,15 @@ fn select_better_recent_finality_proof_works() {
);
assert_eq!(
unjustified_headers,
vec![
TestSourceHeader(false, 8),
TestSourceHeader(false, 9),
TestSourceHeader(false, 10)
]
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)]
);
// if there's intersection between recent finality proofs and unjustified headers and there's
// a proof in this intersection:
// - this better (last from intersection) proof is selected;
// - 'obsolete' unjustified headers are pruned.
let mut unjustified_headers = vec![
TestSourceHeader(false, 8),
TestSourceHeader(false, 9),
TestSourceHeader(false, 10),
];
let mut unjustified_headers =
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
assert_eq!(
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
&[(7, TestFinalityProof(7)), (9, TestFinalityProof(9))],
@@ -416,7 +410,10 @@ fn read_finality_proofs_from_stream_works() {
// when stream is currently empty, nothing is changed
let mut recent_finality_proofs = vec![(1, TestFinalityProof(1))];
let mut stream = futures::stream::pending().into();
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
&mut stream,
&mut recent_finality_proofs,
);
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1))]);
assert!(!stream.needs_restart);
@@ -424,20 +421,20 @@ fn read_finality_proofs_from_stream_works() {
let mut stream = futures::stream::iter(vec![TestFinalityProof(4)])
.chain(futures::stream::pending())
.into();
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
assert_eq!(
recent_finality_proofs,
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
&mut stream,
&mut recent_finality_proofs,
);
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]);
assert!(!stream.needs_restart);
// when stream has ended, we'll need to restart it
let mut stream = futures::stream::empty().into();
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
assert_eq!(
recent_finality_proofs,
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
&mut stream,
&mut recent_finality_proofs,
);
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]);
assert!(stream.needs_restart);
}
+3 -1
View File
@@ -19,7 +19,9 @@
//! are still submitted to the target node, but are treated as auxiliary data as we are not trying
//! to submit all source headers to the target node.
pub use crate::finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient};
pub use crate::finality_loop::{
metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient,
};
use bp_header_chain::FinalityProof;
use std::fmt::Debug;
+220 -229
View File
@@ -20,22 +20,33 @@
//! may stay until source/target chain state isn't updated. When a header reaches the
//! `ready` sub-queue, it may be submitted to the target chain.
use crate::sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SourceHeader};
use crate::sync_types::{
HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SourceHeader,
};
use linked_hash_map::LinkedHashMap;
use num_traits::{One, Zero};
use relay_utils::HeaderId;
use std::{
collections::{btree_map::Entry as BTreeMapEntry, hash_map::Entry as HashMapEntry, BTreeMap, HashMap, HashSet},
collections::{
btree_map::Entry as BTreeMapEntry, hash_map::Entry as HashMapEntry, BTreeMap, HashMap,
HashSet,
},
time::{Duration, Instant},
};
type HeadersQueue<P> =
BTreeMap<<P as HeadersSyncPipeline>::Number, HashMap<<P as HeadersSyncPipeline>::Hash, QueuedHeader<P>>>;
type SyncedChildren<P> =
BTreeMap<<P as HeadersSyncPipeline>::Number, HashMap<<P as HeadersSyncPipeline>::Hash, HashSet<HeaderIdOf<P>>>>;
type KnownHeaders<P> =
BTreeMap<<P as HeadersSyncPipeline>::Number, HashMap<<P as HeadersSyncPipeline>::Hash, HeaderStatus>>;
type HeadersQueue<P> = BTreeMap<
<P as HeadersSyncPipeline>::Number,
HashMap<<P as HeadersSyncPipeline>::Hash, QueuedHeader<P>>,
>;
type SyncedChildren<P> = BTreeMap<
<P as HeadersSyncPipeline>::Number,
HashMap<<P as HeadersSyncPipeline>::Hash, HashSet<HeaderIdOf<P>>>,
>;
type KnownHeaders<P> = BTreeMap<
<P as HeadersSyncPipeline>::Number,
HashMap<<P as HeadersSyncPipeline>::Hash, HeaderStatus>,
>;
/// We're trying to fetch completion data for single header at this interval.
const RETRY_FETCH_COMPLETION_INTERVAL: Duration = Duration::from_secs(20);
@@ -113,35 +124,31 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
pub fn headers_in_status(&self, status: HeaderStatus) -> usize {
match status {
HeaderStatus::Unknown | HeaderStatus::Synced => 0,
HeaderStatus::MaybeOrphan => self
.maybe_orphan
.values()
.fold(0, |total, headers| total + headers.len()),
HeaderStatus::Orphan => self.orphan.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeExtra => self
.maybe_extra
.values()
.fold(0, |total, headers| total + headers.len()),
HeaderStatus::Extra => self.extra.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Ready => self.ready.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Incomplete => self.incomplete.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Submitted => self.submitted.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeOrphan =>
self.maybe_orphan.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Orphan =>
self.orphan.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::MaybeExtra =>
self.maybe_extra.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Extra =>
self.extra.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Ready =>
self.ready.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Incomplete =>
self.incomplete.values().fold(0, |total, headers| total + headers.len()),
HeaderStatus::Submitted =>
self.submitted.values().fold(0, |total, headers| total + headers.len()),
}
}
/// Returns number of headers that are currently in the queue.
pub fn total_headers(&self) -> usize {
self.maybe_orphan
.values()
.fold(0, |total, headers| total + headers.len())
+ self.orphan.values().fold(0, |total, headers| total + headers.len())
+ self
.maybe_extra
.values()
.fold(0, |total, headers| total + headers.len())
+ self.extra.values().fold(0, |total, headers| total + headers.len())
+ self.ready.values().fold(0, |total, headers| total + headers.len())
+ self.incomplete.values().fold(0, |total, headers| total + headers.len())
self.maybe_orphan.values().fold(0, |total, headers| total + headers.len()) +
self.orphan.values().fold(0, |total, headers| total + headers.len()) +
self.maybe_extra.values().fold(0, |total, headers| total + headers.len()) +
self.extra.values().fold(0, |total, headers| total + headers.len()) +
self.ready.values().fold(0, |total, headers| total + headers.len()) +
self.incomplete.values().fold(0, |total, headers| total + headers.len())
}
/// Returns number of best block in the queue.
@@ -157,8 +164,16 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
std::cmp::max(
self.ready.keys().next_back().cloned().unwrap_or_else(Zero::zero),
std::cmp::max(
self.incomplete.keys().next_back().cloned().unwrap_or_else(Zero::zero),
self.submitted.keys().next_back().cloned().unwrap_or_else(Zero::zero),
self.incomplete
.keys()
.next_back()
.cloned()
.unwrap_or_else(Zero::zero),
self.submitted
.keys()
.next_back()
.cloned()
.unwrap_or_else(Zero::zero),
),
),
),
@@ -226,7 +241,7 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
id,
status,
);
return;
return
}
if id.0 < self.prune_border {
@@ -236,7 +251,7 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
P::SOURCE_NAME,
id,
);
return;
return
}
let parent_id = header.parent_id();
@@ -247,20 +262,20 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
HeaderStatus::Unknown | HeaderStatus::MaybeOrphan => {
insert_header(&mut self.maybe_orphan, id, header);
HeaderStatus::MaybeOrphan
}
},
HeaderStatus::Orphan => {
insert_header(&mut self.orphan, id, header);
HeaderStatus::Orphan
}
HeaderStatus::MaybeExtra
| HeaderStatus::Extra
| HeaderStatus::Ready
| HeaderStatus::Incomplete
| HeaderStatus::Submitted
| HeaderStatus::Synced => {
},
HeaderStatus::MaybeExtra |
HeaderStatus::Extra |
HeaderStatus::Ready |
HeaderStatus::Incomplete |
HeaderStatus::Submitted |
HeaderStatus::Synced => {
insert_header(&mut self.maybe_extra, id, header);
HeaderStatus::MaybeExtra
}
},
};
self.known_headers.entry(id.0).or_default().insert(id.1, status);
@@ -288,7 +303,7 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
HeaderStatus::Orphan,
id,
);
return;
return
}
move_header_descendants::<P>(
@@ -351,8 +366,8 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
id,
);
return;
}
return
},
};
// do not remove from `incomplete_headers` here, because otherwise we'll miss
@@ -414,14 +429,20 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
}
/// Marks given headers incomplete.
pub fn add_incomplete_headers(&mut self, make_header_incomplete: bool, new_incomplete_headers: Vec<HeaderIdOf<P>>) {
pub fn add_incomplete_headers(
&mut self,
make_header_incomplete: bool,
new_incomplete_headers: Vec<HeaderIdOf<P>>,
) {
for new_incomplete_header in new_incomplete_headers {
if make_header_incomplete {
self.header_synced(&new_incomplete_header);
}
let move_origins = select_synced_children::<P>(&self.synced_children, &new_incomplete_header);
let move_origins = move_origins.into_iter().chain(std::iter::once(new_incomplete_header));
let move_origins =
select_synced_children::<P>(&self.synced_children, &new_incomplete_header);
let move_origins =
move_origins.into_iter().chain(std::iter::once(new_incomplete_header));
for move_origin in move_origins {
move_header_descendants::<P>(
&mut [&mut self.ready, &mut self.submitted],
@@ -450,7 +471,9 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
// are moved from Ready/Submitted to Incomplete queue
let new_incomplete_headers = ids
.iter()
.filter(|id| !self.incomplete_headers.contains_key(id) && !self.completion_data.contains_key(id))
.filter(|id| {
!self.incomplete_headers.contains_key(id) && !self.completion_data.contains_key(id)
})
.cloned()
.collect::<Vec<_>>();
self.add_incomplete_headers(true, new_incomplete_headers);
@@ -468,8 +491,10 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
// sub2eth rejects H if H.Parent is incomplete
// sub2sub allows 'syncing' headers like that
// => let's check if there are some synced children of just completed header
let move_origins = select_synced_children::<P>(&self.synced_children, &just_completed_header);
let move_origins = move_origins.into_iter().chain(std::iter::once(just_completed_header));
let move_origins =
select_synced_children::<P>(&self.synced_children, &just_completed_header);
let move_origins =
move_origins.into_iter().chain(std::iter::once(just_completed_header));
for move_origin in move_origins {
move_header_descendants::<P>(
&mut [&mut self.incomplete],
@@ -500,7 +525,8 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
pub fn incomplete_header(&mut self) -> Option<HeaderIdOf<P>> {
queued_incomplete_header(&mut self.incomplete_headers, |last_fetch_time| {
let retry = match *last_fetch_time {
Some(last_fetch_time) => last_fetch_time.elapsed() > RETRY_FETCH_COMPLETION_INTERVAL,
Some(last_fetch_time) =>
last_fetch_time.elapsed() > RETRY_FETCH_COMPLETION_INTERVAL,
None => true,
};
@@ -521,7 +547,7 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
/// Prune and never accept headers before this block.
pub fn prune(&mut self, prune_border: P::Number) {
if prune_border <= self.prune_border {
return;
return
}
prune_queue(&mut self.maybe_orphan, prune_border);
@@ -570,10 +596,10 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
match header {
Some(header) => {
let parent_id = header.header().parent_id();
self.incomplete_headers.contains_key(&parent_id)
|| self.completion_data.contains_key(&parent_id)
|| self.status(&parent_id) == HeaderStatus::Incomplete
}
self.incomplete_headers.contains_key(&parent_id) ||
self.completion_data.contains_key(&parent_id) ||
self.status(&parent_id) == HeaderStatus::Incomplete
},
None => false,
}
}
@@ -603,12 +629,8 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
.expect("header has a given status; given queue has the header; qed");
// remember ids of all the children of the current header
let synced_children_entry = self
.synced_children
.entry(current.0)
.or_default()
.entry(current.1)
.or_default();
let synced_children_entry =
self.synced_children.entry(current.0).or_default().entry(current.1).or_default();
let all_queues = [
&self.maybe_orphan,
&self.orphan,
@@ -624,7 +646,9 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
.map(|potential_children| {
potential_children
.values()
.filter(|potential_child| potential_child.header().parent_id() == current)
.filter(|potential_child| {
potential_child.header().parent_id() == current
})
.map(|child| child.id())
.collect::<Vec<_>>()
})
@@ -661,12 +685,19 @@ impl<P: HeadersSyncPipeline> QueuedHeaders<P> {
}
/// Insert header to the queue.
fn insert_header<P: HeadersSyncPipeline>(queue: &mut HeadersQueue<P>, id: HeaderIdOf<P>, header: QueuedHeader<P>) {
fn insert_header<P: HeadersSyncPipeline>(
queue: &mut HeadersQueue<P>,
id: HeaderIdOf<P>,
header: QueuedHeader<P>,
) {
queue.entry(id.0).or_default().insert(id.1, header);
}
/// Remove header from the queue.
fn remove_header<P: HeadersSyncPipeline>(queue: &mut HeadersQueue<P>, id: &HeaderIdOf<P>) -> Option<QueuedHeader<P>> {
fn remove_header<P: HeadersSyncPipeline>(
queue: &mut HeadersQueue<P>,
id: &HeaderIdOf<P>,
) -> Option<QueuedHeader<P>> {
let mut headers_at = match queue.entry(id.0) {
BTreeMapEntry::Occupied(headers_at) => headers_at,
BTreeMapEntry::Vacant(_) => return None,
@@ -680,7 +711,10 @@ fn remove_header<P: HeadersSyncPipeline>(queue: &mut HeadersQueue<P>, id: &Heade
}
/// Get header from the queue.
fn header<'a, P: HeadersSyncPipeline>(queue: &'a HeadersQueue<P>, id: &HeaderIdOf<P>) -> Option<&'a QueuedHeader<P>> {
fn header<'a, P: HeadersSyncPipeline>(
queue: &'a HeadersQueue<P>,
id: &HeaderIdOf<P>,
) -> Option<&'a QueuedHeader<P>> {
queue.get(&id.0).and_then(|by_hash| by_hash.get(&id.1))
}
@@ -799,11 +833,7 @@ fn oldest_headers<P: HeadersSyncPipeline>(
queue: &HeadersQueue<P>,
mut f: impl FnMut(&QueuedHeader<P>) -> bool,
) -> Option<Vec<&QueuedHeader<P>>> {
let result = queue
.values()
.flat_map(|h| h.values())
.take_while(|h| f(h))
.collect::<Vec<_>>();
let result = queue.values().flat_map(|h| h.values()).take_while(|h| f(h)).collect::<Vec<_>>();
if result.is_empty() {
None
} else {
@@ -817,7 +847,10 @@ fn prune_queue<P: HeadersSyncPipeline>(queue: &mut HeadersQueue<P>, prune_border
}
/// Forget all known headers with number less than given.
fn prune_known_headers<P: HeadersSyncPipeline>(known_headers: &mut KnownHeaders<P>, prune_border: P::Number) {
fn prune_known_headers<P: HeadersSyncPipeline>(
known_headers: &mut KnownHeaders<P>,
prune_border: P::Number,
) {
let new_known_headers = known_headers.split_off(&prune_border);
for (pruned_number, pruned_headers) in &*known_headers {
for pruned_hash in pruned_headers.keys() {
@@ -848,8 +881,8 @@ fn queued_incomplete_header<Id: Clone + Eq + std::hash::Hash, T>(
map: &mut LinkedHashMap<Id, T>,
filter: impl FnMut(&mut T) -> bool,
) -> Option<(Id, &T)> {
// TODO (#84): headers that have been just appended to the end of the queue would have to wait until
// all previous headers will be retried
// TODO (#84): headers that have been just appended to the end of the queue would have to wait
// until all previous headers will be retried
let retry_old_header = map
.front()
@@ -857,9 +890,10 @@ fn queued_incomplete_header<Id: Clone + Eq + std::hash::Hash, T>(
.and_then(|key| map.get_mut(&key).map(filter))
.unwrap_or(false);
if retry_old_header {
let (header_key, header) = map.pop_front().expect("we have checked that front() exists; qed");
let (header_key, header) =
map.pop_front().expect("we have checked that front() exists; qed");
map.insert(header_key, header);
return map.back().map(|(id, data)| (id.clone(), data));
return map.back().map(|(id, data)| (id.clone(), data))
}
None
@@ -868,15 +902,15 @@ fn queued_incomplete_header<Id: Clone + Eq + std::hash::Hash, T>(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::sync_loop_tests::{TestHash, TestHeader, TestHeaderId, TestHeadersSyncPipeline, TestNumber};
use crate::sync_types::QueuedHeader;
use crate::{
sync_loop_tests::{
TestHash, TestHeader, TestHeaderId, TestHeadersSyncPipeline, TestNumber,
},
sync_types::QueuedHeader,
};
pub(crate) fn header(number: TestNumber) -> QueuedHeader<TestHeadersSyncPipeline> {
QueuedHeader::new(TestHeader {
number,
hash: hash(number),
parent_hash: hash(number - 1),
})
QueuedHeader::new(TestHeader { number, hash: hash(number), parent_hash: hash(number - 1) })
}
pub(crate) fn hash(number: TestNumber) -> TestHash {
@@ -891,34 +925,41 @@ pub(crate) mod tests {
fn total_headers_works() {
// total headers just sums up number of headers in every queue
let mut queue = QueuedHeaders::<TestHeadersSyncPipeline>::default();
queue.maybe_orphan.entry(1).or_default().insert(
hash(1),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.maybe_orphan.entry(1).or_default().insert(
hash(2),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.maybe_orphan.entry(2).or_default().insert(
hash(3),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.orphan.entry(3).or_default().insert(
hash(4),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.maybe_extra.entry(4).or_default().insert(
hash(5),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.ready.entry(5).or_default().insert(
hash(6),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.incomplete.entry(6).or_default().insert(
hash(7),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(1), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(2), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.maybe_orphan
.entry(2)
.or_default()
.insert(hash(3), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.orphan
.entry(3)
.or_default()
.insert(hash(4), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.maybe_extra
.entry(4)
.or_default()
.insert(hash(5), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.ready
.entry(5)
.or_default()
.insert(hash(6), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.incomplete
.entry(6)
.or_default()
.insert(hash(7), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.total_headers(), 7);
}
@@ -926,48 +967,56 @@ pub(crate) mod tests {
fn best_queued_number_works() {
// initially there are headers in MaybeOrphan queue only
let mut queue = QueuedHeaders::<TestHeadersSyncPipeline>::default();
queue.maybe_orphan.entry(1).or_default().insert(
hash(1),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.maybe_orphan.entry(1).or_default().insert(
hash(2),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue.maybe_orphan.entry(3).or_default().insert(
hash(3),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(1), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.maybe_orphan
.entry(1)
.or_default()
.insert(hash(2), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
queue
.maybe_orphan
.entry(3)
.or_default()
.insert(hash(3), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 3);
// and then there's better header in Orphan
queue.orphan.entry(10).or_default().insert(
hash(10),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.orphan
.entry(10)
.or_default()
.insert(hash(10), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 10);
// and then there's better header in MaybeExtra
queue.maybe_extra.entry(20).or_default().insert(
hash(20),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.maybe_extra
.entry(20)
.or_default()
.insert(hash(20), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 20);
// and then there's better header in Ready
queue.ready.entry(30).or_default().insert(
hash(30),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.ready
.entry(30)
.or_default()
.insert(hash(30), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 30);
// and then there's better header in MaybeOrphan again
queue.maybe_orphan.entry(40).or_default().insert(
hash(40),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.maybe_orphan
.entry(40)
.or_default()
.insert(hash(40), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 40);
// and then there's some header in Incomplete
queue.incomplete.entry(50).or_default().insert(
hash(50),
QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()),
);
queue
.incomplete
.entry(50)
.or_default()
.insert(hash(50), QueuedHeader::<TestHeadersSyncPipeline>::new(Default::default()));
assert_eq!(queue.best_queued_number(), 50);
}
@@ -977,11 +1026,7 @@ pub(crate) mod tests {
let mut queue = QueuedHeaders::<TestHeadersSyncPipeline>::default();
assert_eq!(queue.status(&id(10)), HeaderStatus::Unknown);
// and status is read from the KnownHeaders
queue
.known_headers
.entry(10)
.or_default()
.insert(hash(10), HeaderStatus::Ready);
queue.known_headers.entry(10).or_default().insert(hash(10), HeaderStatus::Ready);
assert_eq!(queue.status(&id(10)), HeaderStatus::Ready);
}
@@ -990,22 +1035,13 @@ pub(crate) mod tests {
// initially we have oldest header #10
let mut queue = QueuedHeaders::<TestHeadersSyncPipeline>::default();
queue.maybe_orphan.entry(10).or_default().insert(hash(1), header(100));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash,
hash(100)
);
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash, hash(100));
// inserting #20 changes nothing
queue.maybe_orphan.entry(20).or_default().insert(hash(1), header(101));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash,
hash(100)
);
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash, hash(100));
// inserting #5 makes it oldest
queue.maybe_orphan.entry(5).or_default().insert(hash(1), header(102));
assert_eq!(
queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash,
hash(102)
);
assert_eq!(queue.header(HeaderStatus::MaybeOrphan).unwrap().header().hash, hash(102));
}
#[test]
@@ -1091,11 +1127,7 @@ pub(crate) mod tests {
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue
.known_headers
.entry(99)
@@ -1108,17 +1140,9 @@ pub(crate) mod tests {
.or_default()
.insert(hash(98), HeaderStatus::MaybeExtra);
queue.maybe_extra.entry(98).or_default().insert(hash(98), header(98));
queue
.known_headers
.entry(97)
.or_default()
.insert(hash(97), HeaderStatus::Extra);
queue.known_headers.entry(97).or_default().insert(hash(97), HeaderStatus::Extra);
queue.extra.entry(97).or_default().insert(hash(97), header(97));
queue
.known_headers
.entry(96)
.or_default()
.insert(hash(96), HeaderStatus::Ready);
queue.known_headers.entry(96).or_default().insert(hash(96), HeaderStatus::Ready);
queue.ready.entry(96).or_default().insert(hash(96), header(96));
queue.target_best_header_response(&id(100));
@@ -1137,31 +1161,19 @@ pub(crate) mod tests {
// children of synced headers are stored
assert_eq!(
vec![id(97)],
queue.synced_children[&96][&hash(96)]
.iter()
.cloned()
.collect::<Vec<_>>()
queue.synced_children[&96][&hash(96)].iter().cloned().collect::<Vec<_>>()
);
assert_eq!(
vec![id(98)],
queue.synced_children[&97][&hash(97)]
.iter()
.cloned()
.collect::<Vec<_>>()
queue.synced_children[&97][&hash(97)].iter().cloned().collect::<Vec<_>>()
);
assert_eq!(
vec![id(99)],
queue.synced_children[&98][&hash(98)]
.iter()
.cloned()
.collect::<Vec<_>>()
queue.synced_children[&98][&hash(98)].iter().cloned().collect::<Vec<_>>()
);
assert_eq!(
vec![id(100)],
queue.synced_children[&99][&hash(99)]
.iter()
.cloned()
.collect::<Vec<_>>()
queue.synced_children[&99][&hash(99)].iter().cloned().collect::<Vec<_>>()
);
assert_eq!(0, queue.synced_children[&100][&hash(100)].len());
}
@@ -1185,11 +1197,7 @@ pub(crate) mod tests {
.entry(102)
.or_default()
.insert(hash(102), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(102)
.or_default()
.insert(hash(102), header(102));
queue.maybe_orphan.entry(102).or_default().insert(hash(102), header(102));
queue
.known_headers
.entry(103)
@@ -1221,11 +1229,7 @@ pub(crate) mod tests {
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue
.known_headers
.entry(101)
@@ -1237,11 +1241,7 @@ pub(crate) mod tests {
.entry(102)
.or_default()
.insert(hash(102), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(102)
.or_default()
.insert(hash(102), header(102));
queue.maybe_orphan.entry(102).or_default().insert(hash(102), header(102));
queue.maybe_orphan_response(&id(99), true);
// then all headers (#100..#103) are moved to the MaybeExtra queue
@@ -1266,21 +1266,13 @@ pub(crate) mod tests {
.entry(100)
.or_default()
.insert(hash(100), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(100)
.or_default()
.insert(hash(100), header(100));
queue.maybe_orphan.entry(100).or_default().insert(hash(100), header(100));
queue
.known_headers
.entry(101)
.or_default()
.insert(hash(101), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(101)
.or_default()
.insert(hash(101), header(101));
queue.maybe_orphan.entry(101).or_default().insert(hash(101), header(101));
queue.maybe_orphan_response(&id(99), false);
// then all headers (#100..#101) are moved to the Orphan queue
@@ -1395,7 +1387,9 @@ pub(crate) mod tests {
queue.incomplete_headers.clear();
queue.incomplete_headers.insert(
id(100),
Some(Instant::now() - RETRY_FETCH_COMPLETION_INTERVAL - RETRY_FETCH_COMPLETION_INTERVAL),
Some(
Instant::now() - RETRY_FETCH_COMPLETION_INTERVAL - RETRY_FETCH_COMPLETION_INTERVAL,
),
);
assert_eq!(queue.incomplete_header(), Some(id(100)));
}
@@ -1551,11 +1545,7 @@ pub(crate) mod tests {
.entry(104)
.or_default()
.insert(hash(104), HeaderStatus::MaybeOrphan);
queue
.maybe_orphan
.entry(104)
.or_default()
.insert(hash(104), header(104));
queue.maybe_orphan.entry(104).or_default().insert(hash(104), header(104));
queue
.known_headers
.entry(103)
@@ -1624,7 +1614,8 @@ pub(crate) mod tests {
fn incomplete_headers_are_still_incomplete_after_advance() {
let mut queue = QueuedHeaders::<TestHeadersSyncPipeline>::default();
// relay#1 knows that header#100 is incomplete && it has headers 101..104 in incomplete queue
// relay#1 knows that header#100 is incomplete && it has headers 101..104 in incomplete
// queue
queue.incomplete_headers.insert(id(100), None);
queue.incomplete.entry(101).or_default().insert(hash(101), header(101));
queue.incomplete.entry(102).or_default().insert(hash(102), header(102));
@@ -1656,8 +1647,8 @@ pub(crate) mod tests {
.or_default()
.insert(hash(104), HeaderStatus::Incomplete);
// let's say relay#2 completes header#100 and then submits header#101+header#102 and it turns
// out that header#102 is also incomplete
// let's say relay#2 completes header#100 and then submits header#101+header#102 and it
// turns out that header#102 is also incomplete
queue.incomplete_headers_response(vec![id(102)].into_iter().collect());
// then the header#103 and the header#104 must have Incomplete status
+26 -20
View File
@@ -19,8 +19,10 @@
//! to submit to the target chain? The context makes decisions basing on parameters
//! passed using `HeadersSyncParams` structure.
use crate::headers::QueuedHeaders;
use crate::sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader};
use crate::{
headers::QueuedHeaders,
sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader},
};
use num_traits::{One, Saturating, Zero};
/// Common sync params.
@@ -121,20 +123,21 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
// if we haven't received best header from source node yet, there's nothing we can download
let source_best_number = self.source_best_number?;
// if we haven't received known best header from target node yet, there's nothing we can download
// if we haven't received known best header from target node yet, there's nothing we can
// download
let target_best_header = self.target_best_header.as_ref()?;
// if there's too many headers in the queue, stop downloading
let in_memory_headers = self.headers.total_headers();
if in_memory_headers >= self.params.max_future_headers_to_download {
return None;
return None
}
// if queue is empty and best header on target is > than best header on source,
// then we shoud reorganization
let best_queued_number = self.headers.best_queued_number();
if best_queued_number.is_zero() && source_best_number < target_best_header.0 {
return Some(source_best_number);
return Some(source_best_number)
}
// we assume that there were no reorganizations if we have already downloaded best header
@@ -143,14 +146,14 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
target_best_header.0,
);
if best_downloaded_number >= source_best_number {
return None;
return None
}
// download new header
Some(best_downloaded_number + One::one())
}
/// Selech orphan header to download.
/// Select orphan header to download.
pub fn select_orphan_header_to_download(&self) -> Option<&QueuedHeader<P>> {
let orphan_header = self.headers.header(HeaderStatus::Orphan)?;
@@ -159,7 +162,7 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
// => let's avoid fetching duplicate headers
let parent_id = orphan_header.parent_id();
if self.headers.status(&parent_id) != HeaderStatus::Unknown {
return None;
return None
}
Some(orphan_header)
@@ -169,12 +172,12 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
pub fn select_headers_to_submit(&self, stalled: bool) -> Option<Vec<&QueuedHeader<P>>> {
// maybe we have paused new headers submit?
if self.pause_submit {
return None;
return None
}
// if we operate in backup mode, we only submit headers when sync has stalled
if self.params.target_tx_mode == TargetTransactionMode::Backup && !stalled {
return None;
return None
}
let headers_in_submit_status = self.headers.headers_in_status(HeaderStatus::Submitted);
@@ -187,15 +190,17 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
let mut total_headers = 0;
self.headers.headers(HeaderStatus::Ready, |header| {
if total_headers == headers_to_submit_count {
return false;
return false
}
if total_headers == self.params.max_headers_in_single_submit {
return false;
return false
}
let encoded_size = P::estimate_size(header);
if total_headers != 0 && total_size + encoded_size > self.params.max_headers_size_in_single_submit {
return false;
if total_headers != 0 &&
total_size + encoded_size > self.params.max_headers_size_in_single_submit
{
return false
}
total_size += encoded_size;
@@ -228,15 +233,14 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
// early return if it is still the same
if self.target_best_header == Some(best_header) {
return false;
return false
}
// remember that this header is now known to the Substrate runtime
self.headers.target_best_header_response(&best_header);
// prune ancient headers
self.headers
.prune(best_header.0.saturating_sub(self.params.prune_depth.into()));
self.headers.prune(best_header.0.saturating_sub(self.params.prune_depth.into()));
// finally remember the best header itself
self.target_best_header = Some(best_header);
@@ -281,9 +285,11 @@ impl<P: HeadersSyncPipeline> HeadersSync<P> {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::headers::tests::{header, id};
use crate::sync_loop_tests::{TestHash, TestHeadersSyncPipeline, TestNumber};
use crate::sync_types::HeaderStatus;
use crate::{
headers::tests::{header, id},
sync_loop_tests::{TestHash, TestHeadersSyncPipeline, TestNumber},
sync_types::HeaderStatus,
};
use relay_utils::HeaderId;
fn side_hash(number: TestNumber) -> TestHash {
+38 -21
View File
@@ -16,9 +16,11 @@
//! Entrypoint for running headers synchronization loop.
use crate::sync::{HeadersSync, HeadersSyncParams};
use crate::sync_loop_metrics::SyncLoopMetrics;
use crate::sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SubmittedHeaders};
use crate::{
sync::{HeadersSync, HeadersSyncParams},
sync_loop_metrics::SyncLoopMetrics,
sync_types::{HeaderIdOf, HeaderStatus, HeadersSyncPipeline, QueuedHeader, SubmittedHeaders},
};
use async_trait::async_trait;
use futures::{future::FutureExt, stream::StreamExt};
@@ -66,8 +68,10 @@ pub trait SourceClient<P: HeadersSyncPipeline>: RelayClient {
async fn header_by_number(&self, number: P::Number) -> Result<P::Header, Self::Error>;
/// Get completion data by header hash.
async fn header_completion(&self, id: HeaderIdOf<P>)
-> Result<(HeaderIdOf<P>, Option<P::Completion>), Self::Error>;
async fn header_completion(
&self,
id: HeaderIdOf<P>,
) -> Result<(HeaderIdOf<P>, Option<P::Completion>), Self::Error>;
/// Get extra data by header hash.
async fn header_extra(
@@ -84,20 +88,32 @@ pub trait TargetClient<P: HeadersSyncPipeline>: RelayClient {
async fn best_header_id(&self) -> Result<HeaderIdOf<P>, Self::Error>;
/// Returns true if header is known to the target node.
async fn is_known_header(&self, id: HeaderIdOf<P>) -> Result<(HeaderIdOf<P>, bool), Self::Error>;
async fn is_known_header(
&self,
id: HeaderIdOf<P>,
) -> Result<(HeaderIdOf<P>, bool), Self::Error>;
/// Submit headers.
async fn submit_headers(&self, headers: Vec<QueuedHeader<P>>) -> SubmittedHeaders<HeaderIdOf<P>, Self::Error>;
async fn submit_headers(
&self,
headers: Vec<QueuedHeader<P>>,
) -> SubmittedHeaders<HeaderIdOf<P>, Self::Error>;
/// Returns ID of headers that require to be 'completed' before children can be submitted.
async fn incomplete_headers_ids(&self) -> Result<HashSet<HeaderIdOf<P>>, Self::Error>;
/// Submit completion data for header.
async fn complete_header(&self, id: HeaderIdOf<P>, completion: P::Completion)
-> Result<HeaderIdOf<P>, Self::Error>;
async fn complete_header(
&self,
id: HeaderIdOf<P>,
completion: P::Completion,
) -> Result<HeaderIdOf<P>, Self::Error>;
/// Returns true if header requires extra data to be submitted.
async fn requires_extra(&self, header: QueuedHeader<P>) -> Result<(HeaderIdOf<P>, bool), Self::Error>;
async fn requires_extra(
&self,
header: QueuedHeader<P>,
) -> Result<(HeaderIdOf<P>, bool), Self::Error>;
}
/// Synchronization maintain procedure.
@@ -110,7 +126,8 @@ pub trait SyncMaintain<P: HeadersSyncPipeline>: 'static + Clone + Send + Sync {
impl<P: HeadersSyncPipeline> SyncMaintain<P> for () {}
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs sync loop.
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
/// sync loop.
pub fn metrics_prefix<P: HeadersSyncPipeline>() -> String {
format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME)
}
@@ -480,7 +497,8 @@ async fn run_until_connection_lost<P: HeadersSyncPipeline, TC: TargetClient<P>>(
id,
);
target_complete_header_future.set(target_client.complete_header(id, completion.clone()).fuse());
target_complete_header_future
.set(target_client.complete_header(id, completion.clone()).fuse());
} else if let Some(header) = sync.headers().header(HeaderStatus::MaybeExtra) {
log::debug!(
target: "bridge",
@@ -501,8 +519,8 @@ async fn run_until_connection_lost<P: HeadersSyncPipeline, TC: TargetClient<P>>(
);
target_existence_status_future.set(target_client.is_known_header(parent_id).fuse());
} else if let Some(headers) =
sync.select_headers_to_submit(last_update_time.elapsed() > BACKUP_STALL_SYNC_TIMEOUT)
} else if let Some(headers) = sync
.select_headers_to_submit(last_update_time.elapsed() > BACKUP_STALL_SYNC_TIMEOUT)
{
log::debug!(
target: "bridge",
@@ -580,7 +598,7 @@ async fn run_until_connection_lost<P: HeadersSyncPipeline, TC: TargetClient<P>>(
P::SOURCE_NAME,
P::TARGET_NAME,
);
return Ok(());
return Ok(())
}
log::debug!(
@@ -616,15 +634,14 @@ fn print_sync_progress<P: HeadersSyncPipeline>(
let now_time = Instant::now();
let (now_best_header, now_target_header) = eth_sync.status();
let need_update = now_time - prev_time > Duration::from_secs(10)
|| match (prev_best_header, now_best_header) {
(Some(prev_best_header), Some(now_best_header)) => {
now_best_header.0.saturating_sub(prev_best_header) > 10.into()
}
let need_update = now_time - prev_time > Duration::from_secs(10) ||
match (prev_best_header, now_best_header) {
(Some(prev_best_header), Some(now_best_header)) =>
now_best_header.0.saturating_sub(prev_best_header) > 10.into(),
_ => false,
};
if !need_update {
return (prev_time, prev_best_header, prev_target_header);
return (prev_time, prev_best_header, prev_target_header)
}
log::info!(
@@ -16,8 +16,10 @@
//! Metrics for headers synchronization relay loop.
use crate::sync::HeadersSync;
use crate::sync_types::{HeaderStatus, HeadersSyncPipeline};
use crate::{
sync::HeadersSync,
sync_types::{HeaderStatus, HeadersSyncPipeline},
};
use num_traits::Zero;
use relay_utils::metrics::{metric_name, register, GaugeVec, Opts, PrometheusError, Registry, U64};
@@ -78,7 +80,8 @@ impl SyncLoopMetrics {
pub fn update<P: HeadersSyncPipeline>(&self, sync: &HeadersSync<P>) {
let headers = sync.headers();
let source_best_number = sync.source_best_number().unwrap_or_else(Zero::zero);
let target_best_number = sync.target_best_header().map(|id| id.0).unwrap_or_else(Zero::zero);
let target_best_number =
sync.target_best_header().map(|id| id.0).unwrap_or_else(Zero::zero);
self.update_best_block_at_source(source_best_number);
self.update_best_block_at_target(target_best_number);
+27 -19
View File
@@ -16,16 +16,18 @@
#![cfg(test)]
use crate::sync_loop::{run, SourceClient, TargetClient};
use crate::sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders};
use crate::{
sync_loop::{run, SourceClient, TargetClient},
sync_types::{HeadersSyncPipeline, QueuedHeader, SourceHeader, SubmittedHeaders},
};
use async_trait::async_trait;
use backoff::backoff::Backoff;
use futures::{future::FutureExt, stream::StreamExt};
use parking_lot::Mutex;
use relay_utils::{
metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient, retry_backoff, HeaderId,
MaybeConnectionError,
metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient,
retry_backoff, HeaderId, MaybeConnectionError,
};
use std::{
collections::{HashMap, HashSet},
@@ -166,7 +168,10 @@ impl SourceClient<TestHeadersSyncPipeline> for Source {
data.header_by_number.get(&number).cloned().ok_or(TestError(false))
}
async fn header_completion(&self, id: TestHeaderId) -> Result<(TestHeaderId, Option<TestCompletion>), TestError> {
async fn header_completion(
&self,
id: TestHeaderId,
) -> Result<(TestHeaderId, Option<TestCompletion>), TestError> {
let mut data = self.data.lock();
(self.on_method_call)(SourceMethod::HeaderCompletion(id), &mut *data);
if data.provides_completion {
@@ -264,7 +269,10 @@ impl TargetClient<TestHeadersSyncPipeline> for Target {
.unwrap_or(Ok((id, false)))
}
async fn submit_headers(&self, headers: Vec<TestQueuedHeader>) -> SubmittedHeaders<TestHeaderId, TestError> {
async fn submit_headers(
&self,
headers: Vec<TestQueuedHeader>,
) -> SubmittedHeaders<TestHeaderId, TestError> {
let mut data = self.data.lock();
(self.on_method_call)(TargetMethod::SubmitHeaders(headers.clone()), &mut *data);
data.submitted_headers
@@ -287,14 +295,21 @@ impl TargetClient<TestHeadersSyncPipeline> for Target {
}
}
async fn complete_header(&self, id: TestHeaderId, completion: TestCompletion) -> Result<TestHeaderId, TestError> {
async fn complete_header(
&self,
id: TestHeaderId,
completion: TestCompletion,
) -> Result<TestHeaderId, TestError> {
let mut data = self.data.lock();
(self.on_method_call)(TargetMethod::CompleteHeader(id, completion), &mut *data);
data.completed_headers.insert(id.1, completion);
Ok(id)
}
async fn requires_extra(&self, header: TestQueuedHeader) -> Result<(TestHeaderId, bool), TestError> {
async fn requires_extra(
&self,
header: TestQueuedHeader,
) -> Result<(TestHeaderId, bool), TestError> {
let mut data = self.data.lock();
(self.on_method_call)(TargetMethod::RequiresExtra(header.clone()), &mut *data);
if data.requires_extra {
@@ -321,11 +336,7 @@ fn test_header(number: TestNumber) -> TestHeader {
TestHeader {
hash: id.1,
number: id.0,
parent_hash: if number == 0 {
TestHash::default()
} else {
test_id(number - 1).1
},
parent_hash: if number == 0 { TestHash::default() } else { test_id(number - 1).1 },
}
}
@@ -467,18 +478,15 @@ fn run_sync_loop_test(params: SyncLoopTestParams) {
let target_requires_extra = params.target_requires_extra;
let target_requires_completion = params.target_requires_completion;
let stop_at = params.stop_at;
let source = Source::new(
params.best_source_header.id(),
params.headers_on_source,
move |method, _| {
let source =
Source::new(params.best_source_header.id(), params.headers_on_source, move |method, _| {
if !target_requires_extra {
source_reject_extra(&method);
}
if !target_requires_completion {
source_reject_completion(&method);
}
},
);
});
let target = Target::new(
params.best_target_header.id(),
params.headers_on_target.into_iter().map(|header| header.id()).collect(),
+13 -9
View File
@@ -50,7 +50,14 @@ pub trait HeadersSyncPipeline: 'static + Clone + Send + Sync {
const TARGET_NAME: &'static str;
/// Headers we're syncing are identified by this hash.
type Hash: Eq + Clone + Copy + Send + Sync + std::fmt::Debug + std::fmt::Display + std::hash::Hash;
type Hash: Eq
+ Clone
+ Copy
+ Send
+ Sync
+ std::fmt::Debug
+ std::fmt::Display
+ std::hash::Hash;
/// Headers we're syncing are identified by this number.
type Number: relay_utils::BlockNumberBase;
/// Type of header that we're syncing.
@@ -77,7 +84,8 @@ pub trait HeadersSyncPipeline: 'static + Clone + Send + Sync {
}
/// A HeaderId for `HeaderSyncPipeline`.
pub type HeaderIdOf<P> = HeaderId<<P as HeadersSyncPipeline>::Hash, <P as HeadersSyncPipeline>::Number>;
pub type HeaderIdOf<P> =
HeaderId<<P as HeadersSyncPipeline>::Hash, <P as HeadersSyncPipeline>::Number>;
/// Header that we're receiving from source node.
pub trait SourceHeader<Hash, Number>: Clone + std::fmt::Debug + PartialEq + Send + Sync {
@@ -153,8 +161,8 @@ impl<P: HeadersSyncPipeline> QueuedHeader<P> {
pub struct SubmittedHeaders<Id, Error> {
/// IDs of headers that have been submitted to target node.
pub submitted: Vec<Id>,
/// IDs of incomplete headers. These headers were submitted (so this id is also in `submitted` vec),
/// but all descendants are not.
/// IDs of incomplete headers. These headers were submitted (so this id is also in `submitted`
/// vec), but all descendants are not.
pub incomplete: Vec<Id>,
/// IDs of ignored headers that we have decided not to submit (they are either rejected by
/// target node immediately, or their descendants of incomplete headers).
@@ -180,10 +188,6 @@ impl<Id: std::fmt::Debug, Error> std::fmt::Display for SubmittedHeaders<Id, Erro
let incomplete = format_ids(self.incomplete.iter());
let rejected = format_ids(self.rejected.iter());
write!(
f,
"Submitted: {}, Incomplete: {}, Rejected: {}",
submitted, incomplete, rejected
)
write!(f, "Submitted: {}, Incomplete: {}, Rejected: {}", submitted, incomplete, rejected)
}
}
@@ -33,7 +33,8 @@ enum TransactionStatus {
/// Run infinite conversion rate updater loop.
///
/// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens * Rate`.
/// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens *
/// Rate`.
pub fn run_conversion_rate_update_loop<
SubmitConversionRateFuture: Future<Output = anyhow::Result<()>> + Send + 'static,
>(
@@ -60,10 +61,10 @@ pub fn run_conversion_rate_update_loop<
match submit_conversion_rate_future.await {
Ok(()) => {
transaction_status = TransactionStatus::Submitted(prev_conversion_rate);
}
},
Err(error) => {
log::trace!(target: "bridge", "Failed to submit conversion rate update transaction: {:?}", error);
}
},
}
}
}
@@ -78,41 +79,43 @@ async fn maybe_select_new_conversion_rate(
right_to_base_conversion_rate: &F64SharedRef,
max_difference_ratio: f64,
) -> Option<(f64, f64)> {
let left_to_right_stored_conversion_rate = (*left_to_right_stored_conversion_rate.read().await)?;
let left_to_right_stored_conversion_rate =
(*left_to_right_stored_conversion_rate.read().await)?;
match *transaction_status {
TransactionStatus::Idle => (),
TransactionStatus::Submitted(previous_left_to_right_stored_conversion_rate) => {
// we can't compare float values from different sources directly, so we only care whether the
// stored rate has been changed or not. If it has been changed, then we assume that our proposal
// has been accepted.
// we can't compare float values from different sources directly, so we only care
// whether the stored rate has been changed or not. If it has been changed, then we
// assume that our proposal has been accepted.
//
// float comparison is ok here, because we compare same-origin (stored in runtime storage) values
// and if they are different, it means that the value has actually been updated
// float comparison is ok here, because we compare same-origin (stored in runtime
// storage) values and if they are different, it means that the value has actually been
// updated
#[allow(clippy::float_cmp)]
if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate {
// the rate has not been changed => we won't submit any transactions until it is accepted,
// or the rate is changed by someone else
return None;
if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate
{
// the rate has not been changed => we won't submit any transactions until it is
// accepted, or the rate is changed by someone else
return None
}
*transaction_status = TransactionStatus::Idle;
}
},
}
let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?;
let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?;
let actual_left_to_right_conversion_rate = right_to_base_conversion_rate / left_to_base_conversion_rate;
let actual_left_to_right_conversion_rate =
right_to_base_conversion_rate / left_to_base_conversion_rate;
let rate_difference = (actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
let rate_difference =
(actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
let rate_difference_ratio = rate_difference / left_to_right_stored_conversion_rate;
if rate_difference_ratio < max_difference_ratio {
return None;
return None
}
Some((
left_to_right_stored_conversion_rate,
actual_left_to_right_conversion_rate,
))
Some((left_to_right_stored_conversion_rate, actual_left_to_right_conversion_rate))
}
#[cfg(test)]
@@ -171,7 +174,13 @@ mod tests {
#[test]
fn transaction_is_not_submitted_when_left_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(10.0), None, Some(1.0), 0.0),
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
None,
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
@@ -179,7 +188,13 @@ mod tests {
#[test]
fn transaction_is_not_submitted_when_right_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(10.0), Some(1.0), None, 0.0),
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
Some(1.0),
None,
0.0
),
(None, TransactionStatus::Idle),
);
}
@@ -187,7 +202,13 @@ mod tests {
#[test]
fn transaction_is_not_submitted_when_stored_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(TransactionStatus::Idle, None, Some(1.0), Some(1.0), 0.0),
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
None,
Some(1.0),
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
@@ -195,7 +216,13 @@ mod tests {
#[test]
fn transaction_is_not_submitted_when_difference_is_below_threshold() {
assert_eq!(
test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(1.0), Some(1.0), Some(1.01), 0.02),
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(1.0),
Some(1.0),
Some(1.01),
0.02
),
(None, TransactionStatus::Idle),
);
}
@@ -203,7 +230,13 @@ mod tests {
#[test]
fn transaction_is_submitted_when_difference_is_above_threshold() {
assert_eq!(
test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(1.0), Some(1.0), Some(1.03), 0.02),
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(1.0),
Some(1.0),
Some(1.03),
0.02
),
(Some((1.0, 1.03)), TransactionStatus::Idle),
);
}
@@ -21,7 +21,9 @@ use crate::finality_target::SubstrateFinalityTarget;
use bp_header_chain::justification::GrandpaJustification;
use bp_runtime::AccountIdOf;
use finality_relay::{FinalitySyncParams, FinalitySyncPipeline};
use relay_substrate_client::{finality_source::FinalitySource, BlockNumberOf, Chain, Client, HashOf, SyncHeader};
use relay_substrate_client::{
finality_source::FinalitySource, BlockNumberOf, Chain, Client, HashOf, SyncHeader,
};
use relay_utils::{metrics::MetricsParams, BlockNumberBase};
use sp_core::Bytes;
use std::{fmt::Debug, marker::PhantomData, time::Duration};
@@ -97,14 +99,12 @@ impl<SourceChain, TargetChain: Chain, TargetSign> Debug
}
}
impl<SourceChain, TargetChain: Chain, TargetSign> SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign> {
impl<SourceChain, TargetChain: Chain, TargetSign>
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>
{
/// Create new Substrate-to-Substrate headers pipeline.
pub fn new(target_client: Client<TargetChain>, target_sign: TargetSign) -> Self {
SubstrateFinalityToSubstrate {
target_client,
target_sign,
_marker: Default::default(),
}
SubstrateFinalityToSubstrate { target_client, target_sign, _marker: Default::default() }
}
}
@@ -157,7 +157,10 @@ where
FinalitySource::new(source_client, None),
SubstrateFinalityTarget::new(target_client, pipeline, transactions_mortality),
FinalitySyncParams {
tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL),
tick: std::cmp::max(
SourceChain::AVERAGE_BLOCK_INTERVAL,
TargetChain::AVERAGE_BLOCK_INTERVAL,
),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: relay_substrate_client::transaction_stall_timeout(
transactions_mortality,
@@ -36,11 +36,7 @@ pub struct SubstrateFinalityTarget<C: Chain, P> {
impl<C: Chain, P> SubstrateFinalityTarget<C, P> {
/// Create new Substrate headers target.
pub fn new(client: Client<C>, pipeline: P, transactions_mortality: Option<u32>) -> Self {
SubstrateFinalityTarget {
client,
pipeline,
transactions_mortality,
}
SubstrateFinalityTarget { client, pipeline, transactions_mortality }
}
}
@@ -97,18 +93,21 @@ where
let pipeline = self.pipeline.clone();
let transactions_mortality = self.transactions_mortality;
self.client
.submit_signed_extrinsic(transactions_author, move |best_block_id, transaction_nonce| {
pipeline.make_submit_finality_proof_transaction(
relay_substrate_client::TransactionEra::new(
best_block_id.0,
best_block_id.1,
transactions_mortality,
),
transaction_nonce,
header,
proof,
)
})
.submit_signed_extrinsic(
transactions_author,
move |best_block_id, transaction_nonce| {
pipeline.make_submit_finality_proof_transaction(
relay_substrate_client::TransactionEra::new(
best_block_id.0,
best_block_id.1,
transactions_mortality,
),
transaction_nonce,
header,
proof,
)
},
)
.await
.map(drop)
}
@@ -21,10 +21,10 @@
//! and authorities set from source to target chain. The headers sync starts
//! with this header.
use bp_header_chain::InitializationData;
use bp_header_chain::{
find_grandpa_authorities_scheduled_change,
justification::{verify_justification, GrandpaJustification},
InitializationData,
};
use codec::Decode;
use finality_grandpa::voter_set::VoterSet;
@@ -103,31 +103,30 @@ async fn prepare_initialization_data<SourceChain: Chain>(
// In ideal world we just need to get best finalized header and then to read GRANDPA authorities
// set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at this header.
//
// But now there are problems with this approach - `CurrentSetId` may return invalid value. So here
// we're waiting for the next justification, read the authorities set and then try to figure out
// the set id with bruteforce.
let justifications = source_client
.subscribe_justifications()
.await
.map_err(|err| format!("Failed to subscribe to {} justifications: {:?}", SourceChain::NAME, err))?;
// But now there are problems with this approach - `CurrentSetId` may return invalid value. So
// here we're waiting for the next justification, read the authorities set and then try to
// figure out the set id with bruteforce.
let justifications = source_client.subscribe_justifications().await.map_err(|err| {
format!("Failed to subscribe to {} justifications: {:?}", SourceChain::NAME, err)
})?;
// Read next justification - the header that it finalizes will be used as initial header.
let justification = justifications
.next()
.await
.map_err(|err| err.to_string())
.and_then(|justification| justification.ok_or_else(|| "stream has ended unexpectedly".into()))
.and_then(|justification| {
justification.ok_or_else(|| "stream has ended unexpectedly".into())
})
.map_err(|err| {
format!(
"Failed to read {} justification from the stream: {}",
SourceChain::NAME,
err,
)
format!("Failed to read {} justification from the stream: {}", SourceChain::NAME, err,)
})?;
// Read initial header.
let justification: GrandpaJustification<SourceChain::Header> = Decode::decode(&mut &justification.0[..])
.map_err(|err| format!("Failed to decode {} justification: {:?}", SourceChain::NAME, err))?;
let justification: GrandpaJustification<SourceChain::Header> =
Decode::decode(&mut &justification.0[..]).map_err(|err| {
format!("Failed to decode {} justification: {:?}", SourceChain::NAME, err)
})?;
let (initial_header_hash, initial_header_number) =
(justification.commit.target_hash, justification.commit.target_number);
@@ -140,7 +139,8 @@ async fn prepare_initialization_data<SourceChain: Chain>(
);
// Read GRANDPA authorities set at initial header.
let initial_authorities_set = source_authorities_set(&source_client, initial_header_hash).await?;
let initial_authorities_set =
source_authorities_set(&source_client, initial_header_hash).await?;
log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}",
SourceChain::NAME,
initial_authorities_set,
@@ -159,7 +159,8 @@ async fn prepare_initialization_data<SourceChain: Chain>(
);
let schedules_change = scheduled_change.is_some();
if schedules_change {
authorities_for_verification = source_authorities_set(&source_client, *initial_header.parent_hash()).await?;
authorities_for_verification =
source_authorities_set(&source_client, *initial_header.parent_hash()).await?;
log::trace!(
target: "bridge",
"Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}",
@@ -171,13 +172,14 @@ async fn prepare_initialization_data<SourceChain: Chain>(
// Now let's try to guess authorities set id by verifying justification.
let mut initial_authorities_set_id = 0;
let mut min_possible_block_number = SourceChain::BlockNumber::zero();
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()).ok_or_else(|| {
format!(
"Read invalid {} authorities set: {:?}",
SourceChain::NAME,
authorities_for_verification,
)
})?;
let authorities_for_verification = VoterSet::new(authorities_for_verification.clone())
.ok_or_else(|| {
format!(
"Read invalid {} authorities set: {:?}",
SourceChain::NAME,
authorities_for_verification,
)
})?;
loop {
log::trace!(
target: "bridge", "Trying {} GRANDPA authorities set id: {}",
@@ -194,21 +196,21 @@ async fn prepare_initialization_data<SourceChain: Chain>(
.is_ok();
if is_valid_set_id {
break;
break
}
initial_authorities_set_id += 1;
min_possible_block_number += One::one();
if min_possible_block_number > initial_header_number {
// there can't be more authorities set changes than headers => if we have reached `initial_block_number`
// and still have not found correct value of `initial_authorities_set_id`, then something
// else is broken => fail
// there can't be more authorities set changes than headers => if we have reached
// `initial_block_number` and still have not found correct value of
// `initial_authorities_set_id`, then something else is broken => fail
return Err(format!(
"Failed to guess initial {} GRANDPA authorities set id: checked all\
possible ids in range [0; {}]",
SourceChain::NAME,
initial_header_number
));
))
}
}
@@ -244,10 +246,8 @@ async fn source_authorities_set<SourceChain: Chain>(
source_client: &Client<SourceChain>,
header_hash: SourceChain::Hash,
) -> Result<GrandpaAuthoritiesSet, String> {
let raw_authorities_set = source_client
.grandpa_authorities_set(header_hash)
.await
.map_err(|err| {
let raw_authorities_set =
source_client.grandpa_authorities_set(header_hash).await.map_err(|err| {
format!(
"Failed to retrive {} GRANDPA authorities set at header {}: {:?}",
SourceChain::NAME,
@@ -27,15 +27,9 @@ pub fn token_price_metric(
FloatJsonValueMetric::new(
registry,
prefix,
format!(
"https://api.coingecko.com/api/v3/simple/price?ids={}&vs_currencies=btc",
token_id
),
format!("https://api.coingecko.com/api/v3/simple/price?ids={}&vs_currencies=btc", token_id),
format!("$.{}.btc", token_id),
format!("{}_to_base_conversion_rate", token_id.replace("-", "_")),
format!(
"Rate used to convert from {} to some BASE tokens",
token_id.to_uppercase()
),
format!("Rate used to convert from {} to some BASE tokens", token_id.to_uppercase()),
)
}
@@ -16,9 +16,10 @@
//! Tools for supporting message lanes between two Substrate-based chains.
use crate::messages_source::SubstrateMessagesProof;
use crate::messages_target::SubstrateMessagesReceivingProof;
use crate::on_demand_headers::OnDemandHeadersRelay;
use crate::{
messages_source::SubstrateMessagesProof, messages_target::SubstrateMessagesReceivingProof,
on_demand_headers::OnDemandHeadersRelay,
};
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce};
@@ -65,18 +66,22 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync {
/// Underlying generic message lane.
type MessageLane: MessageLane;
/// Name of the runtime method that returns dispatch weight of outbound messages at the source chain.
/// Name of the runtime method that returns dispatch weight of outbound messages at the source
/// chain.
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str;
/// Name of the runtime method that returns latest generated nonce at the source chain.
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str;
/// Name of the runtime method that returns latest received (confirmed) nonce at the the source chain.
/// Name of the runtime method that returns latest received (confirmed) nonce at the the source
/// chain.
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str;
/// Name of the runtime method that returns latest received nonce at the target chain.
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str;
/// Name of the runtime method that returns the latest confirmed (reward-paid) nonce at the target chain.
/// Name of the runtime method that returns the latest confirmed (reward-paid) nonce at the
/// target chain.
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str;
/// Number of the runtime method that returns state of "unrewarded relayers" set at the target chain.
/// Number of the runtime method that returns state of "unrewarded relayers" set at the target
/// chain.
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str;
/// Name of the runtime method that returns id of best finalized source header at target chain.
@@ -101,7 +106,8 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync {
/// Target chain.
type TargetChain: Chain;
/// Returns id of account that we're using to sign transactions at target chain (messages proof).
/// Returns id of account that we're using to sign transactions at target chain (messages
/// proof).
fn target_transactions_author(&self) -> AccountIdOf<Self::TargetChain>;
/// Make messages delivery transaction.
@@ -113,7 +119,8 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync {
proof: <Self::MessageLane as MessageLane>::MessagesProof,
) -> Bytes;
/// Returns id of account that we're using to sign transactions at source chain (delivery proof).
/// Returns id of account that we're using to sign transactions at source chain (delivery
/// proof).
fn source_transactions_author(&self) -> AccountIdOf<Self::SourceChain>;
/// Make messages receiving proof transaction.
@@ -127,7 +134,12 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync {
/// Substrate-to-Substrate message lane.
#[derive(Debug)]
pub struct SubstrateMessageLaneToSubstrate<Source: Chain, SourceSignParams, Target: Chain, TargetSignParams> {
pub struct SubstrateMessageLaneToSubstrate<
Source: Chain,
SourceSignParams,
Target: Chain,
TargetSignParams,
> {
/// Client for the source Substrate chain.
pub source_client: Client<Source>,
/// Parameters required to sign transactions for source chain.
@@ -194,8 +206,8 @@ pub fn select_delivery_transaction_limits<W: pallet_bridge_messages::WeightInfoE
let weight_for_delivery_tx = max_extrinsic_weight / 3;
let weight_for_messages_dispatch = max_extrinsic_weight - weight_for_delivery_tx;
let delivery_tx_base_weight =
W::receive_messages_proof_overhead() + W::receive_messages_proof_outbound_lane_state_overhead();
let delivery_tx_base_weight = W::receive_messages_proof_overhead() +
W::receive_messages_proof_outbound_lane_state_overhead();
let delivery_tx_weight_rest = weight_for_delivery_tx - delivery_tx_base_weight;
let max_number_of_messages = std::cmp::min(
delivery_tx_weight_rest / W::receive_messages_proof_messages_overhead(1),
@@ -221,15 +233,18 @@ pub struct StandaloneMessagesMetrics {
pub target_to_base_conversion_rate: Option<F64SharedRef>,
/// Shared reference to the actual source -> <base> chain token conversion rate.
pub source_to_base_conversion_rate: Option<F64SharedRef>,
/// Shared reference to the stored (in the source chain runtime storage) target -> source chain conversion rate.
/// Shared reference to the stored (in the source chain runtime storage) target -> source chain
/// conversion rate.
pub target_to_source_conversion_rate: Option<F64SharedRef>,
}
impl StandaloneMessagesMetrics {
/// Return conversion rate from target to source tokens.
pub async fn target_to_source_conversion_rate(&self) -> Option<f64> {
let target_to_base_conversion_rate = (*self.target_to_base_conversion_rate.as_ref()?.read().await)?;
let source_to_base_conversion_rate = (*self.source_to_base_conversion_rate.as_ref()?.read().await)?;
let target_to_base_conversion_rate =
(*self.target_to_base_conversion_rate.as_ref()?.read().await)?;
let source_to_base_conversion_rate =
(*self.source_to_base_conversion_rate.as_ref()?.read().await)?;
Some(source_to_base_conversion_rate / target_to_base_conversion_rate)
}
}
@@ -246,8 +261,8 @@ pub fn add_standalone_metrics<P: SubstrateMessageLane>(
let mut target_to_source_conversion_rate = None;
let mut source_to_base_conversion_rate = None;
let mut target_to_base_conversion_rate = None;
let mut metrics_params =
relay_utils::relay_metrics(metrics_prefix, metrics_params).standalone_metric(|registry, prefix| {
let mut metrics_params = relay_utils::relay_metrics(metrics_prefix, metrics_params)
.standalone_metric(|registry, prefix| {
StorageProofOverheadMetric::new(
registry,
prefix,
@@ -256,8 +271,10 @@ pub fn add_standalone_metrics<P: SubstrateMessageLane>(
format!("{} storage proof overhead", P::SourceChain::NAME),
)
})?;
if let Some((target_to_source_conversion_rate_storage_key, initial_target_to_source_conversion_rate)) =
target_to_source_conversion_rate_params
if let Some((
target_to_source_conversion_rate_storage_key,
initial_target_to_source_conversion_rate,
)) = target_to_source_conversion_rate_params
{
metrics_params = metrics_params.standalone_metric(|registry, prefix| {
let metric = FloatStorageValueMetric::<_, sp_runtime::FixedU128>::new(
@@ -285,14 +302,16 @@ pub fn add_standalone_metrics<P: SubstrateMessageLane>(
}
if let Some(source_chain_token_id) = source_chain_token_id {
metrics_params = metrics_params.standalone_metric(|registry, prefix| {
let metric = crate::helpers::token_price_metric(registry, prefix, source_chain_token_id)?;
let metric =
crate::helpers::token_price_metric(registry, prefix, source_chain_token_id)?;
source_to_base_conversion_rate = Some(metric.shared_value_ref());
Ok(metric)
})?;
}
if let Some(target_chain_token_id) = target_chain_token_id {
metrics_params = metrics_params.standalone_metric(|registry, prefix| {
let metric = crate::helpers::token_price_metric(registry, prefix, target_chain_token_id)?;
let metric =
crate::helpers::token_price_metric(registry, prefix, target_chain_token_id)?;
target_to_base_conversion_rate = Some(metric.shared_value_ref());
Ok(metric)
})?;
@@ -312,19 +331,21 @@ mod tests {
use super::*;
use async_std::sync::{Arc, RwLock};
type RialtoToMillauMessagesWeights = pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>;
type RialtoToMillauMessagesWeights =
pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>;
#[test]
fn select_delivery_transaction_limits_works() {
let (max_count, max_weight) = select_delivery_transaction_limits::<RialtoToMillauMessagesWeights>(
bp_millau::max_extrinsic_weight(),
bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
let (max_count, max_weight) =
select_delivery_transaction_limits::<RialtoToMillauMessagesWeights>(
bp_millau::max_extrinsic_weight(),
bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
);
assert_eq!(
(max_count, max_weight),
// We don't actually care about these values, so feel free to update them whenever test
// fails. The only thing to do before that is to ensure that new values looks sane: i.e. weight
// reserved for messages dispatch allows dispatch of non-trivial messages.
// fails. The only thing to do before that is to ensure that new values looks sane:
// i.e. weight reserved for messages dispatch allows dispatch of non-trivial messages.
//
// Any significant change in this values should attract additional attention.
(782, 216_583_333_334),
@@ -18,9 +18,10 @@
//! runtime that implements `<BridgedChainName>HeaderApi` to allow bridging with
//! <BridgedName> chain.
use crate::messages_lane::SubstrateMessageLane;
use crate::messages_target::SubstrateMessagesReceivingProof;
use crate::on_demand_headers::OnDemandHeadersRelay;
use crate::{
messages_lane::SubstrateMessageLane, messages_target::SubstrateMessagesReceivingProof,
on_demand_headers::OnDemandHeadersRelay,
};
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState};
@@ -29,16 +30,17 @@ use bridge_runtime_common::messages::{
};
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use messages_relay::message_lane::MessageLane;
use messages_relay::{
message_lane::{SourceHeaderIdOf, TargetHeaderIdOf},
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{
ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, SourceClientState,
ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient,
SourceClientState,
},
};
use num_traits::{Bounded, Zero};
use relay_substrate_client::{
BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf, HeaderOf, IndexOf,
BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf, HeaderOf,
IndexOf,
};
use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId};
use sp_core::Bytes;
@@ -69,12 +71,7 @@ impl<P: SubstrateMessageLane> SubstrateMessagesSource<P> {
lane_id: LaneId,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>,
) -> Self {
SubstrateMessagesSource {
client,
lane,
lane_id,
target_to_source_headers_relay,
}
SubstrateMessagesSource { client, lane, lane_id, target_to_source_headers_relay }
}
}
@@ -150,8 +147,8 @@ where
Some(id.1),
)
.await?;
let latest_generated_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let latest_generated_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_generated_nonce))
}
@@ -167,8 +164,8 @@ where
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
@@ -176,7 +173,10 @@ where
&self,
id: SourceHeaderIdOf<P::MessageLane>,
nonces: RangeInclusive<MessageNonce>,
) -> Result<MessageDetailsMap<<P::MessageLane as MessageLane>::SourceChainBalance>, SubstrateError> {
) -> Result<
MessageDetailsMap<<P::MessageLane as MessageLane>::SourceChainBalance>,
SubstrateError,
> {
let encoded_response = self
.client
.state_call(
@@ -187,7 +187,8 @@ where
.await?;
make_message_details_map::<P::SourceChain>(
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?,
Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?,
nonces,
)
}
@@ -205,7 +206,8 @@ where
),
SubstrateError,
> {
let mut storage_keys = Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1);
let mut storage_keys =
Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1);
let mut message_nonce = *nonces.start();
while message_nonce <= *nonces.end() {
let message_key = pallet_bridge_messages::storage_keys::message_key(
@@ -223,12 +225,7 @@ where
));
}
let proof = self
.client
.prove_storage(storage_keys, id.1)
.await?
.iter_nodes()
.collect();
let proof = self.client.prove_storage(storage_keys, id.1).await?.iter_nodes().collect();
let proof = FromBridgedChainMessagesProof {
bridged_header_hash: id.1,
storage_proof: proof,
@@ -246,9 +243,16 @@ where
) -> Result<(), SubstrateError> {
let lane = self.lane.clone();
self.client
.submit_signed_extrinsic(self.lane.source_transactions_author(), move |_, transaction_nonce| {
lane.make_messages_receiving_proof_transaction(transaction_nonce, generated_at_block, proof)
})
.submit_signed_extrinsic(
self.lane.source_transactions_author(),
move |_, transaction_nonce| {
lane.make_messages_receiving_proof_transaction(
transaction_nonce,
generated_at_block,
proof,
)
},
)
.await?;
Ok(())
}
@@ -259,7 +263,9 @@ where
}
}
async fn estimate_confirmation_transaction(&self) -> <P::MessageLane as MessageLane>::SourceChainBalance {
async fn estimate_confirmation_transaction(
&self,
) -> <P::MessageLane as MessageLane>::SourceChainBalance {
self.client
.estimate_extrinsic_fee(self.lane.make_messages_receiving_proof_transaction(
Zero::zero(),
@@ -276,10 +282,14 @@ where
///
/// We don't care about proof actually being the valid proof, because its validity doesn't
/// affect the call weight - we only care about its size.
fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>() -> SubstrateMessagesReceivingProof<TC> {
let single_message_confirmation_size =
bp_messages::InboundLaneData::<()>::encoded_size_hint(SC::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1, 1)
.unwrap_or(u32::MAX);
fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>(
) -> SubstrateMessagesReceivingProof<TC> {
let single_message_confirmation_size = bp_messages::InboundLaneData::<()>::encoded_size_hint(
SC::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE,
1,
1,
)
.unwrap_or(u32::MAX);
let proof_size = TC::STORAGE_PROOF_OVERHEAD.saturating_add(single_message_confirmation_size);
(
UnrewardedRelayersState {
@@ -303,7 +313,10 @@ fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>() -> SubstrateMes
pub async fn read_client_state<SelfChain, BridgedHeaderHash, BridgedHeaderNumber>(
self_client: &Client<SelfChain>,
best_finalized_header_id_method_name: &str,
) -> Result<ClientState<HeaderIdOf<SelfChain>, HeaderId<BridgedHeaderHash, BridgedHeaderNumber>>, SubstrateError>
) -> Result<
ClientState<HeaderIdOf<SelfChain>, HeaderId<BridgedHeaderHash, BridgedHeaderNumber>>,
SubstrateError,
>
where
SelfChain: Chain,
SelfChain::Header: DeserializeOwned,
@@ -313,8 +326,10 @@ where
{
// let's read our state first: we need best finalized header hash on **this** chain
let self_best_finalized_header_hash = self_client.best_finalized_header_hash().await?;
let self_best_finalized_header = self_client.header_by_hash(self_best_finalized_header_hash).await?;
let self_best_finalized_id = HeaderId(*self_best_finalized_header.number(), self_best_finalized_header_hash);
let self_best_finalized_header =
self_client.header_by_hash(self_best_finalized_header_hash).await?;
let self_best_finalized_id =
HeaderId(*self_best_finalized_header.number(), self_best_finalized_header_hash);
// now let's read our best header on **this** chain
let self_best_header = self_client.best_header().await?;
@@ -330,11 +345,10 @@ where
)
.await?;
let decoded_best_finalized_peer_on_self: (BridgedHeaderNumber, BridgedHeaderHash) =
Decode::decode(&mut &encoded_best_finalized_peer_on_self.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let peer_on_self_best_finalized_id = HeaderId(
decoded_best_finalized_peer_on_self.0,
decoded_best_finalized_peer_on_self.1,
);
Decode::decode(&mut &encoded_best_finalized_peer_on_self.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
let peer_on_self_best_finalized_id =
HeaderId(decoded_best_finalized_peer_on_self.0, decoded_best_finalized_peer_on_self.1);
Ok(ClientState {
best_self: self_best_id,
@@ -358,16 +372,14 @@ fn make_message_details_map<C: Chain>(
// this is actually prevented by external logic
if nonces.is_empty() {
return Ok(weights_map);
return Ok(weights_map)
}
// check if last nonce is missing - loop below is not checking this
let last_nonce_is_missing = weights
.last()
.map(|details| details.nonce != *nonces.end())
.unwrap_or(true);
let last_nonce_is_missing =
weights.last().map(|details| details.nonce != *nonces.end()).unwrap_or(true);
if last_nonce_is_missing {
return make_missing_nonce_error(*nonces.end());
return make_missing_nonce_error(*nonces.end())
}
let mut expected_nonce = *nonces.start();
@@ -379,20 +391,21 @@ fn make_message_details_map<C: Chain>(
(false, true) => {
// this may happen if some messages were already pruned from the source node
//
// this is not critical error and will be auto-resolved by messages lane (and target node)
// this is not critical error and will be auto-resolved by messages lane (and target
// node)
log::info!(
target: "bridge",
"Some messages are missing from the {} node: {:?}. Target node may be out of sync?",
C::NAME,
expected_nonce..details.nonce,
);
}
},
(false, false) => {
// some nonces are missing from the middle/tail of the range
//
// this is critical error, because we can't miss any nonces
return make_missing_nonce_error(expected_nonce);
}
return make_missing_nonce_error(expected_nonce)
},
}
weights_map.insert(
@@ -528,7 +541,8 @@ mod tests {
#[test]
fn prepare_dummy_messages_delivery_proof_works() {
let expected_minimal_size = Wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE + Rococo::STORAGE_PROOF_OVERHEAD;
let expected_minimal_size =
Wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE + Rococo::STORAGE_PROOF_OVERHEAD;
let dummy_proof = prepare_dummy_messages_delivery_proof::<Wococo, Rococo>();
assert!(
dummy_proof.1.encode().len() as u32 > expected_minimal_size,
@@ -18,9 +18,11 @@
//! runtime that implements `<BridgedChainName>HeaderApi` to allow bridging with
//! <BridgedName> chain.
use crate::messages_lane::{StandaloneMessagesMetrics, SubstrateMessageLane};
use crate::messages_source::{read_client_state, SubstrateMessagesProof};
use crate::on_demand_headers::OnDemandHeadersRelay;
use crate::{
messages_lane::{StandaloneMessagesMetrics, SubstrateMessageLane},
messages_source::{read_client_state, SubstrateMessagesProof},
on_demand_headers::OnDemandHeadersRelay,
};
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState};
@@ -30,14 +32,14 @@ use bridge_runtime_common::messages::{
};
use codec::{Decode, Encode};
use frame_support::weights::{Weight, WeightToFeePolynomial};
use messages_relay::message_lane::MessageLane;
use messages_relay::{
message_lane::{SourceHeaderIdOf, TargetHeaderIdOf},
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{TargetClient, TargetClientState},
};
use num_traits::{Bounded, Zero};
use relay_substrate_client::{
BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderOf, IndexOf, WeightToFeeOf,
BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderOf, IndexOf,
WeightToFeeOf,
};
use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId};
use sp_core::Bytes;
@@ -45,10 +47,8 @@ use sp_runtime::{traits::Saturating, DeserializeOwned, FixedPointNumber, FixedU1
use std::{convert::TryFrom, ops::RangeInclusive};
/// Message receiving proof returned by the target Substrate node.
pub type SubstrateMessagesReceivingProof<C> = (
UnrewardedRelayersState,
FromBridgedChainMessagesDeliveryProof<HashOf<C>>,
);
pub type SubstrateMessagesReceivingProof<C> =
(UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof<HashOf<C>>);
/// Substrate client as Substrate messages target.
pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> {
@@ -150,8 +150,8 @@ where
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
@@ -167,8 +167,8 @@ where
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
@@ -185,7 +185,8 @@ where
)
.await?;
let unrewarded_relayers_state: UnrewardedRelayersState =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Decode::decode(&mut &encoded_response.0[..])
.map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, unrewarded_relayers_state))
}
@@ -193,10 +194,7 @@ where
&self,
id: TargetHeaderIdOf<P::MessageLane>,
) -> Result<
(
TargetHeaderIdOf<P::MessageLane>,
<P::MessageLane as MessageLane>::MessagesReceivingProof,
),
(TargetHeaderIdOf<P::MessageLane>, <P::MessageLane as MessageLane>::MessagesReceivingProof),
SubstrateError,
> {
let (id, relayers_state) = self.unrewarded_relayers_state(id).await?;
@@ -227,9 +225,17 @@ where
let lane = self.lane.clone();
let nonces_clone = nonces.clone();
self.client
.submit_signed_extrinsic(self.lane.target_transactions_author(), move |_, transaction_nonce| {
lane.make_messages_delivery_transaction(transaction_nonce, generated_at_header, nonces_clone, proof)
})
.submit_signed_extrinsic(
self.lane.target_transactions_author(),
move |_, transaction_nonce| {
lane.make_messages_delivery_transaction(
transaction_nonce,
generated_at_header,
nonces_clone,
proof,
)
},
)
.await?;
Ok(nonces)
}
@@ -247,11 +253,8 @@ where
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<<P::MessageLane as MessageLane>::SourceChainBalance, SubstrateError> {
let conversion_rate = self
.metric_values
.target_to_source_conversion_rate()
.await
.ok_or_else(|| {
let conversion_rate =
self.metric_values.target_to_source_conversion_rate().await.ok_or_else(|| {
SubstrateError::Custom(format!(
"Failed to compute conversion rate from {} to {}",
P::TargetChain::NAME,
@@ -264,26 +267,31 @@ where
Zero::zero(),
HeaderId(Default::default(), Default::default()),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(nonces.clone(), total_dispatch_weight, total_size),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
total_dispatch_weight,
total_size,
),
);
let delivery_tx_fee = self.client.estimate_extrinsic_fee(delivery_tx).await?;
let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee();
// The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch fee payment
// (Currency::transfer in regular deployment). But if message dispatch has already been paid
// at the Source chain, the delivery transaction will refund relayer with this additional cost.
// But `estimate_extrinsic_fee` obviously just returns pre-dispatch cost of the transaction. So
// if transaction delivers prepaid message, then it may happen that pre-dispatch cost is larger
// than reward and `Rational` relayer will refuse to deliver this message.
// The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch
// fee payment (Currency::transfer in regular deployment). But if message dispatch has
// already been paid at the Source chain, the delivery transaction will refund relayer with
// this additional cost. But `estimate_extrinsic_fee` obviously just returns pre-dispatch
// cost of the transaction. So if transaction delivers prepaid message, then it may happen
// that pre-dispatch cost is larger than reward and `Rational` relayer will refuse to
// deliver this message.
//
// The most obvious solution would be to deduct total weight of dispatch fee payments from the
// `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if
// `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is strictly
// positive, so we can't use this option.
// The most obvious solution would be to deduct total weight of dispatch fee payments from
// the `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if
// `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is
// strictly positive, so we can't use this option.
//
// Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target chain.
// This requires more knowledge of the Target chain, but seems there's no better way to solve
// this now.
// Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target
// chain. This requires more knowledge of the Target chain, but seems there's no better way
// to solve this now.
let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 {
const WEIGHT_DIFFERENCE: Weight = 100;
@@ -294,7 +302,11 @@ where
Zero::zero(),
HeaderId(Default::default(), Default::default()),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(nonces.clone(), larger_dispatch_weight, total_size),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
larger_dispatch_weight,
total_size,
),
))
.await?;
@@ -311,10 +323,11 @@ where
Zero::zero()
};
let delivery_fee_in_source_tokens = convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>(
FixedU128::from_float(conversion_rate),
inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens),
);
let delivery_fee_in_source_tokens =
convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>(
FixedU128::from_float(conversion_rate),
inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens),
);
log::trace!(
target: "bridge",
@@ -356,7 +369,10 @@ fn prepare_dummy_messages_proof<SC: Chain>(
total_dispatch_weight,
FromBridgedChainMessagesProof {
bridged_header_hash: Default::default(),
storage_proof: vec![vec![0; SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize]],
storage_proof: vec![vec![
0;
SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize
]],
lane: Default::default(),
nonces_start: *nonces.start(),
nonces_end: *nonces.end(),
@@ -373,8 +389,10 @@ fn convert_target_tokens_to_source_tokens<SC: Chain, TC: Chain>(
where
SC::Balance: TryFrom<TC::Balance>,
{
SC::Balance::try_from(target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee))
.unwrap_or_else(|_| SC::Balance::max_value())
SC::Balance::try_from(
target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee),
)
.unwrap_or_else(|_| SC::Balance::max_value())
}
/// Compute fee multiplier that is used by the chain, given a couple of fees for transactions
@@ -392,7 +410,8 @@ fn compute_fee_multiplier<C: Chain>(
larger_adjusted_weight_fee: BalanceOf<C>,
larger_tx_weight: Weight,
) -> FixedU128 {
let adjusted_weight_fee_difference = larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee);
let adjusted_weight_fee_difference =
larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee);
let smaller_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::calc(&smaller_tx_weight);
let larger_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::calc(&larger_tx_weight);
FixedU128::saturating_from_rational(
@@ -511,10 +530,12 @@ mod tests {
let multiplier = FixedU128::saturating_from_rational(1, 1000);
let smaller_weight = 1_000_000;
let smaller_adjusted_weight_fee = multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&smaller_weight));
let smaller_adjusted_weight_fee =
multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&smaller_weight));
let larger_weight = smaller_weight + 200_000;
let larger_adjusted_weight_fee = multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&larger_weight));
let larger_adjusted_weight_fee =
multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&larger_weight));
assert_eq!(
compute_fee_multiplier::<Rococo>(
@@ -533,7 +554,8 @@ mod tests {
compute_prepaid_messages_refund::<TestSubstrateMessageLane>(
10,
FixedU128::saturating_from_rational(110, 100),
) > (10 * TestSubstrateMessageLane::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN).into()
) > (10 * TestSubstrateMessageLane::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN)
.into()
);
}
}
@@ -31,18 +31,23 @@ use relay_substrate_client::{
Chain, Client, HeaderIdOf, SyncHeader,
};
use relay_utils::{
metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, MaybeConnectionError,
metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient,
MaybeConnectionError,
};
use crate::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, RECENT_FINALITY_PROOFS_LIMIT, STALL_TIMEOUT,
use crate::{
finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, RECENT_FINALITY_PROOFS_LIMIT,
STALL_TIMEOUT,
},
finality_target::SubstrateFinalityTarget,
};
use crate::finality_target::SubstrateFinalityTarget;
/// On-demand Substrate <-> Substrate headers relay.
///
/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages relay) needs
/// it to continue its regular work. When enough headers are relayed, on-demand stops syncing headers.
/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages
/// relay) needs it to continue its regular work. When enough headers are relayed, on-demand stops
/// syncing headers.
#[derive(Clone)]
pub struct OnDemandHeadersRelay<SourceChain: Chain> {
/// Relay task name.
@@ -68,7 +73,11 @@ impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> {
TargetChain::BlockNumber: BlockNumberBase,
TargetSign: Clone + Send + Sync + 'static,
P: SubstrateFinalitySyncPipeline<
FinalitySyncPipeline = SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
FinalitySyncPipeline = SubstrateFinalityToSubstrate<
SourceChain,
TargetChain,
TargetSign,
>,
TargetChain = TargetChain,
>,
{
@@ -135,8 +144,11 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
_,
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
>::new(source_client.clone(), Some(required_header_number.clone()));
let mut finality_target =
SubstrateFinalityTarget::new(target_client.clone(), pipeline.clone(), target_transactions_mortality);
let mut finality_target = SubstrateFinalityTarget::new(
target_client.clone(),
pipeline.clone(),
target_transactions_mortality,
);
let mut latest_non_mandatory_at_source = Zero::zero();
let mut restart_relay = true;
@@ -163,12 +175,16 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
&mut finality_target,
)
.await;
continue;
continue
}
// read best finalized source header number from target
let best_finalized_source_header_at_target =
best_finalized_source_header_at_target::<SourceChain, _, _>(&finality_target, &relay_task_name).await;
let best_finalized_source_header_at_target = best_finalized_source_header_at_target::<
SourceChain,
_,
_,
>(&finality_target, &relay_task_name)
.await;
if matches!(best_finalized_source_header_at_target, Err(ref e) if e.is_connection_error()) {
relay_utils::relay_loop::reconnect_failed_client(
FailedClient::Target,
@@ -177,11 +193,12 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
&mut finality_target,
)
.await;
continue;
continue
}
// submit mandatory header if some headers are missing
let best_finalized_source_header_at_target_fmt = format!("{:?}", best_finalized_source_header_at_target);
let best_finalized_source_header_at_target_fmt =
format!("{:?}", best_finalized_source_header_at_target);
let mandatory_scan_range = mandatory_headers_scan_range::<SourceChain>(
best_finalized_source_header_at_source.ok(),
best_finalized_source_header_at_target.ok(),
@@ -207,8 +224,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
// there are no (or we don't need to relay them) mandatory headers in the range
// => to avoid scanning the same headers over and over again, remember that
latest_non_mandatory_at_source = mandatory_scan_range.1;
}
Err(e) => {
},
Err(e) =>
if e.is_connection_error() {
relay_utils::relay_loop::reconnect_failed_client(
FailedClient::Source,
@@ -217,9 +234,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
&mut finality_target,
)
.await;
continue;
}
}
continue
},
}
}
@@ -230,7 +246,10 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>(
finality_source.clone(),
finality_target.clone(),
FinalitySyncParams {
tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL),
tick: std::cmp::max(
SourceChain::AVERAGE_BLOCK_INTERVAL,
TargetChain::AVERAGE_BLOCK_INTERVAL,
),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: STALL_TIMEOUT,
only_mandatory_headers,
@@ -279,12 +298,12 @@ async fn mandatory_headers_scan_range<C: Chain>(
.checked_sub(&best_finalized_source_header_at_target)
.unwrap_or_else(Zero::zero);
if current_headers_difference <= maximal_headers_difference {
return None;
return None
}
// if relay is already asked to sync headers, don't do anything yet
if required_header_number > best_finalized_source_header_at_target {
return None;
return None
}
Some((
@@ -293,7 +312,8 @@ async fn mandatory_headers_scan_range<C: Chain>(
))
}
/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay it.
/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay
/// it.
///
/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise.
async fn relay_mandatory_header_from_range<SourceChain: Chain, P>(
@@ -308,7 +328,8 @@ where
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
// search for mandatory header first
let mandatory_source_header_number = find_mandatory_header_in_range(finality_source, range).await?;
let mandatory_source_header_number =
find_mandatory_header_in_range(finality_source, range).await?;
// if there are no mandatory headers - we have nothing to do
let mandatory_source_header_number = match mandatory_source_header_number {
@@ -320,7 +341,7 @@ where
// less than our `mandatory_source_header_number` before logging anything
let mut required_header_number = required_header_number.lock().await;
if *required_header_number >= mandatory_source_header_number {
return Ok(false);
return Ok(false)
}
log::trace!(
@@ -348,19 +369,16 @@ where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
finality_source
.on_chain_best_finalized_block_number()
.await
.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from source in {} relay: {:?}",
relay_task_name,
error,
);
finality_source.on_chain_best_finalized_block_number().await.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from source in {} relay: {:?}",
relay_task_name,
error,
);
error
})
error
})
}
/// Read best finalized source block number from target client.
@@ -375,19 +393,16 @@ where
P: SubstrateFinalitySyncPipeline,
P::FinalitySyncPipeline: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
finality_target
.best_finalized_source_block_number()
.await
.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from target in {} relay: {:?}",
relay_task_name,
error,
);
finality_target.best_finalized_source_block_number().await.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from target in {} relay: {:?}",
relay_task_name,
error,
);
error
})
error
})
}
/// Read first mandatory header in given inclusive range.
@@ -403,9 +418,10 @@ where
{
let mut current = range.0;
while current <= range.1 {
let header: SyncHeader<SourceChain::Header> = finality_source.client().header_by_number(current).await?.into();
let header: SyncHeader<SourceChain::Header> =
finality_source.client().header_by_number(current).await?.into();
if header.is_mandatory() {
return Ok(Some(current));
return Ok(Some(current))
}
current += One::one();
@@ -431,7 +447,13 @@ mod tests {
#[async_std::test]
async fn mandatory_headers_scan_range_selects_range_if_too_many_headers_are_missing() {
assert_eq!(
mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, 5, &Arc::new(Mutex::new(0))).await,
mandatory_headers_scan_range::<TestChain>(
AT_SOURCE,
AT_TARGET,
5,
&Arc::new(Mutex::new(0))
)
.await,
Some((AT_TARGET.unwrap() + 1, AT_SOURCE.unwrap())),
);
}
@@ -439,7 +461,13 @@ mod tests {
#[async_std::test]
async fn mandatory_headers_scan_range_selects_nothing_if_enough_headers_are_relayed() {
assert_eq!(
mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, 10, &Arc::new(Mutex::new(0))).await,
mandatory_headers_scan_range::<TestChain>(
AT_SOURCE,
AT_TARGET,
10,
&Arc::new(Mutex::new(0))
)
.await,
None,
);
}
+4 -2
View File
@@ -61,7 +61,9 @@ pub trait MessageLane: 'static + Clone + Send + Sync {
}
/// Source header id within given one-way message lane.
pub type SourceHeaderIdOf<P> = HeaderId<<P as MessageLane>::SourceHeaderHash, <P as MessageLane>::SourceHeaderNumber>;
pub type SourceHeaderIdOf<P> =
HeaderId<<P as MessageLane>::SourceHeaderHash, <P as MessageLane>::SourceHeaderNumber>;
/// Target header id within given one-way message lane.
pub type TargetHeaderIdOf<P> = HeaderId<<P as MessageLane>::TargetHeaderHash, <P as MessageLane>::TargetHeaderNumber>;
pub type TargetHeaderIdOf<P> =
HeaderId<<P as MessageLane>::TargetHeaderHash, <P as MessageLane>::TargetHeaderNumber>;
@@ -24,10 +24,12 @@
//! finalized header. I.e. when talking about headers in lane context, we
//! only care about finalized headers.
use crate::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf};
use crate::message_race_delivery::run as run_message_delivery_race;
use crate::message_race_receiving::run as run_message_receiving_race;
use crate::metrics::MessageLaneLoopMetrics;
use crate::{
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_race_delivery::run as run_message_delivery_race,
message_race_receiving::run as run_message_receiving_race,
metrics::MessageLaneLoopMetrics,
};
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
@@ -64,20 +66,22 @@ pub struct Params {
pub enum RelayerMode {
/// The relayer doesn't care about rewards.
Altruistic,
/// The relayer will deliver all messages and confirmations as long as he's not losing any funds.
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
Rational,
}
/// Message delivery race parameters.
#[derive(Debug, Clone)]
pub struct MessageDeliveryParams {
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number of entries
/// in the `InboundLaneData::relayers` set, all new messages will be rejected until reward payment will
/// be proved (by including outbound lane state to the message delivery transaction).
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number
/// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until
/// reward payment will be proved (by including outbound lane state to the message delivery
/// transaction).
pub max_unrewarded_relayer_entries_at_target: MessageNonce,
/// Message delivery race will stop delivering messages if there are `max_unconfirmed_nonces_at_target`
/// unconfirmed nonces on the target node. The race would continue once they're confirmed by the
/// receiving race.
/// Message delivery race will stop delivering messages if there are
/// `max_unconfirmed_nonces_at_target` unconfirmed nonces on the target node. The race would
/// continue once they're confirmed by the receiving race.
pub max_unconfirmed_nonces_at_target: MessageNonce,
/// Maximal number of relayed messages in single delivery transaction.
pub max_messages_in_single_batch: MessageNonce,
@@ -103,7 +107,8 @@ pub struct MessageDetails<SourceChainBalance> {
}
/// Messages details map.
pub type MessageDetailsMap<SourceChainBalance> = BTreeMap<MessageNonce, MessageDetails<SourceChainBalance>>;
pub type MessageDetailsMap<SourceChainBalance> =
BTreeMap<MessageNonce, MessageDetails<SourceChainBalance>>;
/// Message delivery race proof parameters.
#[derive(Debug, PartialEq)]
@@ -225,7 +230,8 @@ pub struct ClientState<SelfHeaderId, PeerHeaderId> {
pub best_self: SelfHeaderId,
/// Best finalized header id of this chain.
pub best_finalized_self: SelfHeaderId,
/// Best finalized header id of the peer chain read at the best block of this chain (at `best_finalized_self`).
/// Best finalized header id of the peer chain read at the best block of this chain (at
/// `best_finalized_self`).
pub best_finalized_peer_at_best_self: PeerHeaderId,
}
@@ -244,14 +250,10 @@ pub struct ClientsState<P: MessageLane> {
pub target: Option<TargetClientState<P>>,
}
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs sync loop.
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
/// sync loop.
pub fn metrics_prefix<P: MessageLane>(lane: &LaneId) -> String {
format!(
"{}_to_{}_MessageLane_{}",
P::SOURCE_NAME,
P::TARGET_NAME,
hex::encode(lane)
)
format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane))
}
/// Run message lane service loop.
@@ -270,22 +272,20 @@ pub async fn run<P: MessageLane>(
.standalone_metric(|registry, prefix| GlobalMetrics::new(registry, prefix))?
.expose()
.await?
.run(
metrics_prefix::<P>(&params.lane),
move |source_client, target_client, metrics| {
run_until_connection_lost(
params.clone(),
source_client,
target_client,
metrics,
exit_signal.clone(),
)
},
)
.run(metrics_prefix::<P>(&params.lane), move |source_client, target_client, metrics| {
run_until_connection_lost(
params.clone(),
source_client,
target_client,
metrics,
exit_signal.clone(),
)
})
.await
}
/// Run one-way message delivery loop until connection with target or source node is lost, or exit signal is received.
/// Run one-way message delivery loop until connection with target or source node is lost, or exit
/// signal is received.
async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
params: Params,
source_client: SC,
@@ -557,7 +557,7 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_source_fails {
return Err(TestError);
return Err(TestError)
}
Ok(data.source_state.clone())
}
@@ -569,7 +569,7 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_source_fails {
return Err(TestError);
return Err(TestError)
}
Ok((id, data.source_latest_generated_nonce))
}
@@ -609,11 +609,7 @@ pub(crate) mod tests {
nonces: RangeInclusive<MessageNonce>,
proof_parameters: MessageProofParameters,
) -> Result<
(
SourceHeaderIdOf<TestMessageLane>,
RangeInclusive<MessageNonce>,
TestMessagesProof,
),
(SourceHeaderIdOf<TestMessageLane>, RangeInclusive<MessageNonce>, TestMessagesProof),
TestError,
> {
let mut data = self.data.lock();
@@ -694,7 +690,7 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_target_fails {
return Err(TestError);
return Err(TestError)
}
Ok(data.target_state.clone())
}
@@ -706,7 +702,7 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_target_fails {
return Err(TestError);
return Err(TestError)
}
Ok((id, data.target_latest_received_nonce))
}
@@ -732,7 +728,7 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_target_fails {
return Err(TestError);
return Err(TestError)
}
Ok((id, data.target_latest_confirmed_received_nonce))
}
@@ -753,14 +749,15 @@ pub(crate) mod tests {
let mut data = self.data.lock();
(self.tick)(&mut *data);
if data.is_target_fails {
return Err(TestError);
return Err(TestError)
}
data.target_state.best_self =
HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1);
data.target_state.best_finalized_self = data.target_state.best_self;
data.target_latest_received_nonce = *proof.0.end();
if let Some(target_latest_confirmed_received_nonce) = proof.1 {
data.target_latest_confirmed_received_nonce = target_latest_confirmed_received_nonce;
data.target_latest_confirmed_received_nonce =
target_latest_confirmed_received_nonce;
}
data.submitted_messages_proofs.push(proof);
Ok(nonces)
@@ -780,11 +777,9 @@ pub(crate) mod tests {
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<TestSourceChainBalance, TestError> {
Ok(
BASE_MESSAGE_DELIVERY_TRANSACTION_COST * (nonces.end() - nonces.start() + 1)
+ total_dispatch_weight
+ total_size as TestSourceChainBalance,
)
Ok(BASE_MESSAGE_DELIVERY_TRANSACTION_COST * (nonces.end() - nonces.start() + 1) +
total_dispatch_weight +
total_size as TestSourceChainBalance)
}
}
@@ -797,14 +792,8 @@ pub(crate) mod tests {
async_std::task::block_on(async {
let data = Arc::new(Mutex::new(data));
let source_client = TestSourceClient {
data: data.clone(),
tick: source_tick,
};
let target_client = TestTargetClient {
data: data.clone(),
tick: target_tick,
};
let source_client = TestSourceClient { data: data.clone(), tick: source_tick };
let target_client = TestTargetClient { data: data.clone(), tick: target_tick };
let _ = run(
Params {
lane: [0, 0, 0, 0],
@@ -907,7 +896,10 @@ pub(crate) mod tests {
data.source_state.best_finalized_self = data.source_state.best_self;
// headers relay must only be started when we need new target headers at source node
if data.target_to_source_header_required.is_some() {
assert!(data.source_state.best_finalized_peer_at_best_self.0 < data.target_state.best_self.0);
assert!(
data.source_state.best_finalized_peer_at_best_self.0 <
data.target_state.best_self.0
);
data.target_to_source_header_required = None;
}
// syncing target headers -> source chain
@@ -924,7 +916,10 @@ pub(crate) mod tests {
data.target_state.best_finalized_self = data.target_state.best_self;
// headers relay must only be started when we need new source headers at target node
if data.source_to_target_header_required.is_some() {
assert!(data.target_state.best_finalized_peer_at_best_self.0 < data.source_state.best_self.0);
assert!(
data.target_state.best_finalized_peer_at_best_self.0 <
data.source_state.best_self.0
);
data.source_to_target_header_required = None;
}
// syncing source headers -> target chain
@@ -13,18 +13,20 @@
//! Message delivery race delivers proof-of-messages from "lane.source" to "lane.target".
use crate::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf};
use crate::message_lane_loop::{
MessageDeliveryParams, MessageDetailsMap, MessageProofParameters, RelayerMode,
SourceClient as MessageLaneSourceClient, SourceClientState, TargetClient as MessageLaneTargetClient,
TargetClientState,
use crate::{
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{
MessageDeliveryParams, MessageDetailsMap, MessageProofParameters, RelayerMode,
SourceClient as MessageLaneSourceClient, SourceClientState,
TargetClient as MessageLaneTargetClient, TargetClientState,
},
message_race_loop::{
MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces,
TargetClient, TargetClientNonces,
},
message_race_strategy::{BasicStrategy, SourceRangesQueue},
metrics::MessageLaneLoopMetrics,
};
use crate::message_race_loop::{
MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces, TargetClient,
TargetClientNonces,
};
use crate::message_race_strategy::{BasicStrategy, SourceRangesQueue};
use crate::metrics::MessageLaneLoopMetrics;
use async_trait::async_trait;
use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight};
@@ -66,7 +68,8 @@ pub async fn run<P: MessageLane>(
MessageDeliveryStrategy::<P, _, _> {
lane_source_client: source_client,
lane_target_client: target_client,
max_unrewarded_relayer_entries_at_target: params.max_unrewarded_relayer_entries_at_target,
max_unrewarded_relayer_entries_at_target: params
.max_unrewarded_relayer_entries_at_target,
max_unconfirmed_nonces_at_target: params.max_unconfirmed_nonces_at_target,
max_messages_in_single_batch: params.max_messages_in_single_batch,
max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch,
@@ -121,8 +124,10 @@ where
at_block: SourceHeaderIdOf<P>,
prev_latest_nonce: MessageNonce,
) -> Result<(SourceHeaderIdOf<P>, SourceClientNonces<Self::NoncesRange>), Self::Error> {
let (at_block, latest_generated_nonce) = self.client.latest_generated_nonce(at_block).await?;
let (at_block, latest_confirmed_nonce) = self.client.latest_confirmed_received_nonce(at_block).await?;
let (at_block, latest_generated_nonce) =
self.client.latest_generated_nonce(at_block).await?;
let (at_block, latest_confirmed_nonce) =
self.client.latest_confirmed_received_nonce(at_block).await?;
if let Some(metrics_msg) = self.metrics_msg.as_ref() {
metrics_msg.update_source_latest_generated_nonce::<P>(latest_generated_nonce);
@@ -131,7 +136,10 @@ where
let new_nonces = if latest_generated_nonce > prev_latest_nonce {
self.client
.generated_message_details(at_block.clone(), prev_latest_nonce + 1..=latest_generated_nonce)
.generated_message_details(
at_block.clone(),
prev_latest_nonce + 1..=latest_generated_nonce,
)
.await?
} else {
MessageDetailsMap::new()
@@ -139,10 +147,7 @@ where
Ok((
at_block,
SourceClientNonces {
new_nonces,
confirmed_nonce: Some(latest_confirmed_nonce),
},
SourceClientNonces { new_nonces, confirmed_nonce: Some(latest_confirmed_nonce) },
))
}
@@ -151,7 +156,8 @@ where
at_block: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>,
proof_parameters: Self::ProofParameters,
) -> Result<(SourceHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesProof), Self::Error> {
) -> Result<(SourceHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesProof), Self::Error>
{
self.client.prove_messages(at_block, nonces, proof_parameters).await
}
}
@@ -180,10 +186,13 @@ where
&self,
at_block: TargetHeaderIdOf<P>,
update_metrics: bool,
) -> Result<(TargetHeaderIdOf<P>, TargetClientNonces<DeliveryRaceTargetNoncesData>), Self::Error> {
) -> Result<(TargetHeaderIdOf<P>, TargetClientNonces<DeliveryRaceTargetNoncesData>), Self::Error>
{
let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?;
let (at_block, latest_confirmed_nonce) = self.client.latest_confirmed_received_nonce(at_block).await?;
let (at_block, unrewarded_relayers) = self.client.unrewarded_relayers_state(at_block).await?;
let (at_block, latest_confirmed_nonce) =
self.client.latest_confirmed_received_nonce(at_block).await?;
let (at_block, unrewarded_relayers) =
self.client.unrewarded_relayers_state(at_block).await?;
if update_metrics {
if let Some(metrics_msg) = self.metrics_msg.as_ref() {
@@ -210,9 +219,7 @@ where
nonces: RangeInclusive<MessageNonce>,
proof: P::MessagesProof,
) -> Result<RangeInclusive<MessageNonce>, Self::Error> {
self.client
.submit_messages_proof(generated_at_block, nonces, proof)
.await
self.client.submit_messages_proof(generated_at_block, nonces, proof).await
}
}
@@ -245,7 +252,8 @@ struct MessageDeliveryStrategy<P: MessageLane, SC, TC> {
max_messages_size_in_single_batch: u32,
/// Relayer operating mode.
relayer_mode: RelayerMode,
/// Latest confirmed nonces at the source client + the header id where we have first met this nonce.
/// Latest confirmed nonces at the source client + the header id where we have first met this
/// nonce.
latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf<P>, MessageNonce)>,
/// Target nonces from the source client.
target_nonces: Option<TargetClientNonces<DeliveryRaceTargetNoncesData>>,
@@ -269,23 +277,11 @@ impl<P: MessageLane, SC, TC> std::fmt::Debug for MessageDeliveryStrategy<P, SC,
"max_unrewarded_relayer_entries_at_target",
&self.max_unrewarded_relayer_entries_at_target,
)
.field(
"max_unconfirmed_nonces_at_target",
&self.max_unconfirmed_nonces_at_target,
)
.field("max_unconfirmed_nonces_at_target", &self.max_unconfirmed_nonces_at_target)
.field("max_messages_in_single_batch", &self.max_messages_in_single_batch)
.field(
"max_messages_weight_in_single_batch",
&self.max_messages_weight_in_single_batch,
)
.field(
"max_messages_size_in_single_batch",
&self.max_messages_size_in_single_batch,
)
.field(
"latest_confirmed_nonces_at_source",
&self.latest_confirmed_nonces_at_source,
)
.field("max_messages_weight_in_single_batch", &self.max_messages_weight_in_single_batch)
.field("max_messages_size_in_single_batch", &self.max_messages_size_in_single_batch)
.field("latest_confirmed_nonces_at_source", &self.latest_confirmed_nonces_at_source)
.field("target_nonces", &self.target_nonces)
.field("strategy", &self.strategy)
.finish()
@@ -319,8 +315,12 @@ where
self.strategy.is_empty()
}
fn required_source_header_at_target(&self, current_best: &SourceHeaderIdOf<P>) -> Option<SourceHeaderIdOf<P>> {
let header_required_for_messages_delivery = self.strategy.required_source_header_at_target(current_best);
fn required_source_header_at_target(
&self,
current_best: &SourceHeaderIdOf<P>,
) -> Option<SourceHeaderIdOf<P>> {
let header_required_for_messages_delivery =
self.strategy.required_source_header_at_target(current_best);
let header_required_for_reward_confirmations_delivery =
self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone());
match (
@@ -371,10 +371,7 @@ where
self.target_nonces = Some(target_nonces);
self.strategy.best_target_nonces_updated(
TargetClientNonces {
latest_nonce: nonces.latest_nonce,
nonces_data: (),
},
TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () },
race_state,
)
}
@@ -399,14 +396,12 @@ where
}
if let Some(ref mut target_nonces) = self.target_nonces {
target_nonces.latest_nonce = std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce);
target_nonces.latest_nonce =
std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce);
}
self.strategy.finalized_target_nonces_updated(
TargetClientNonces {
latest_nonce: nonces.latest_nonce,
nonces_data: (),
},
TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () },
race_state,
)
}
@@ -428,12 +423,15 @@ where
// There's additional condition in the message delivery race: target would reject messages
// if there are too much unconfirmed messages at the inbound lane.
// The receiving race is responsible to deliver confirmations back to the source chain. So if
// there's a lot of unconfirmed messages, let's wait until it'll be able to do its job.
// The receiving race is responsible to deliver confirmations back to the source chain. So
// if there's a lot of unconfirmed messages, let's wait until it'll be able to do its job.
let latest_received_nonce_at_target = target_nonces.latest_nonce;
let confirmations_missing = latest_received_nonce_at_target.checked_sub(latest_confirmed_nonce_at_source);
let confirmations_missing =
latest_received_nonce_at_target.checked_sub(latest_confirmed_nonce_at_source);
match confirmations_missing {
Some(confirmations_missing) if confirmations_missing >= self.max_unconfirmed_nonces_at_target => {
Some(confirmations_missing)
if confirmations_missing >= self.max_unconfirmed_nonces_at_target =>
{
log::debug!(
target: "bridge",
"Cannot deliver any more messages from {} to {}. Too many unconfirmed nonces \
@@ -445,50 +443,55 @@ where
self.max_unconfirmed_nonces_at_target,
);
return None;
return None
}
_ => (),
}
// Ok - we may have new nonces to deliver. But target may still reject new messages, because we haven't
// notified it that (some) messages have been confirmed. So we may want to include updated
// `source.latest_confirmed` in the proof.
// Ok - we may have new nonces to deliver. But target may still reject new messages, because
// we haven't notified it that (some) messages have been confirmed. So we may want to
// include updated `source.latest_confirmed` in the proof.
//
// Important note: we're including outbound state lane proof whenever there are unconfirmed nonces
// on the target chain. Other strategy is to include it only if it's absolutely necessary.
// Important note: we're including outbound state lane proof whenever there are unconfirmed
// nonces on the target chain. Other strategy is to include it only if it's absolutely
// necessary.
let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce;
let outbound_state_proof_required = latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source;
let outbound_state_proof_required =
latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source;
// The target node would also reject messages if there are too many entries in the
// "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then
// we should wait for confirmations race.
let unrewarded_relayer_entries_limit_reached =
target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries
>= self.max_unrewarded_relayer_entries_at_target;
target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >=
self.max_unrewarded_relayer_entries_at_target;
if unrewarded_relayer_entries_limit_reached {
// so there are already too many unrewarded relayer entries in the set
//
// => check if we can prove enough rewards. If not, we should wait for more rewards to be paid
// => check if we can prove enough rewards. If not, we should wait for more rewards to
// be paid
let number_of_rewards_being_proved =
latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target);
let enough_rewards_being_proved = number_of_rewards_being_proved
>= target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry;
let enough_rewards_being_proved = number_of_rewards_being_proved >=
target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry;
if !enough_rewards_being_proved {
return None;
return None
}
}
// If we're here, then the confirmations race did its job && sending side now knows that messages
// have been delivered. Now let's select nonces that we want to deliver.
// If we're here, then the confirmations race did its job && sending side now knows that
// messages have been delivered. Now let's select nonces that we want to deliver.
//
// We may deliver at most:
//
// max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - latest_confirmed_nonce_at_target)
// max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target -
// latest_confirmed_nonce_at_target)
//
// messages in the batch. But since we're including outbound state proof in the batch, then it
// may be increased to:
// messages in the batch. But since we're including outbound state proof in the batch, then
// it may be increased to:
//
// max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - latest_confirmed_nonce_at_source)
// max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target -
// latest_confirmed_nonce_at_source)
let future_confirmed_nonce_at_target = if outbound_state_proof_required {
latest_confirmed_nonce_at_source
} else {
@@ -505,7 +508,8 @@ where
let lane_source_client = self.lane_source_client.clone();
let lane_target_client = self.lane_target_client.clone();
let maximal_source_queue_index = self.strategy.maximal_available_source_queue_index(race_state)?;
let maximal_source_queue_index =
self.strategy.maximal_available_source_queue_index(race_state)?;
let previous_total_dispatch_weight = self.total_queued_dispatch_weight();
let source_queue = self.strategy.source_queue();
let range_end = select_nonces_for_delivery_transaction(
@@ -529,10 +533,7 @@ where
Some((
selected_nonces,
MessageProofParameters {
outbound_state_proof_required,
dispatch_weight,
},
MessageProofParameters { outbound_state_proof_required, dispatch_weight },
))
}
}
@@ -595,9 +596,9 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
// limit messages in the batch by weight
let new_selected_weight = match selected_weight.checked_add(details.dispatch_weight) {
Some(new_selected_weight) if new_selected_weight <= max_messages_weight_in_single_batch => {
new_selected_weight
}
Some(new_selected_weight)
if new_selected_weight <= max_messages_weight_in_single_batch =>
new_selected_weight,
new_selected_weight if selected_count == 0 => {
log::warn!(
target: "bridge",
@@ -607,13 +608,14 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
max_messages_weight_in_single_batch,
);
new_selected_weight.unwrap_or(Weight::MAX)
}
},
_ => break,
};
// limit messages in the batch by size
let new_selected_size = match selected_size.checked_add(details.size) {
Some(new_selected_size) if new_selected_size <= max_messages_size_in_single_batch => new_selected_size,
Some(new_selected_size) if new_selected_size <= max_messages_size_in_single_batch =>
new_selected_size,
new_selected_size if selected_count == 0 => {
log::warn!(
target: "bridge",
@@ -623,14 +625,14 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
max_messages_size_in_single_batch,
);
new_selected_size.unwrap_or(u32::MAX)
}
},
_ => break,
};
// limit number of messages in the batch
let new_selected_count = selected_count + 1;
if new_selected_count > max_messages_in_this_batch {
break;
break
}
// If dispatch fee has been paid at the source chain, it means that it is **relayer** who's
@@ -639,13 +641,14 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
// If dispatch fee is paid at the target chain, it means that it'll be withdrawn from the
// dispatch origin account AND reward is not covering this fee.
//
// So in the latter case we're not adding the dispatch weight to the delivery transaction weight.
// So in the latter case we're not adding the dispatch weight to the delivery transaction
// weight.
let mut new_selected_prepaid_nonces = selected_prepaid_nonces;
let new_selected_unpaid_weight = match details.dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => {
new_selected_prepaid_nonces += 1;
selected_unpaid_weight.saturating_add(details.dispatch_weight)
}
},
DispatchFeePayment::AtTargetChain => selected_unpaid_weight,
};
@@ -654,11 +657,12 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
match relayer_mode {
RelayerMode::Altruistic => {
soft_selected_count = index + 1;
}
},
RelayerMode::Rational => {
let delivery_transaction_cost = lane_target_client
.estimate_delivery_transaction_in_source_tokens(
hard_selected_begin_nonce..=(hard_selected_begin_nonce + index as MessageNonce),
hard_selected_begin_nonce..=
(hard_selected_begin_nonce + index as MessageNonce),
new_selected_prepaid_nonces,
new_selected_unpaid_weight,
new_selected_size as u32,
@@ -678,7 +682,8 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
let is_total_reward_less_than_cost = total_reward < total_cost;
let prev_total_cost = total_cost;
let prev_total_reward = total_reward;
total_confirmations_cost = total_confirmations_cost.saturating_add(&confirmation_transaction_cost);
total_confirmations_cost =
total_confirmations_cost.saturating_add(&confirmation_transaction_cost);
total_reward = total_reward.saturating_add(&details.reward);
total_cost = total_confirmations_cost.saturating_add(&delivery_transaction_cost);
if !is_total_reward_less_than_cost && total_reward < total_cost {
@@ -713,7 +718,7 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
selected_reward = total_reward;
selected_cost = total_cost;
}
}
},
}
hard_selected_count = index + 1;
@@ -725,9 +730,11 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
}
if hard_selected_count != soft_selected_count {
let hard_selected_end_nonce = hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
let hard_selected_end_nonce =
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
let soft_selected_begin_nonce = hard_selected_begin_nonce;
let soft_selected_end_nonce = soft_selected_begin_nonce + soft_selected_count as MessageNonce - 1;
let soft_selected_end_nonce =
soft_selected_begin_nonce + soft_selected_count as MessageNonce - 1;
log::warn!(
target: "bridge",
"Relayer may deliver nonces [{:?}; {:?}], but because of its strategy ({:?}) it has selected \
@@ -785,9 +792,9 @@ mod tests {
use super::*;
use crate::message_lane_loop::{
tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance, TestSourceClient,
TestSourceHeaderId, TestTargetClient, TestTargetHeaderId, BASE_MESSAGE_DELIVERY_TRANSACTION_COST,
CONFIRMATION_TRANSACTION_COST,
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
BASE_MESSAGE_DELIVERY_TRANSACTION_COST, CONFIRMATION_TRANSACTION_COST,
},
MessageDetails,
};
@@ -795,13 +802,14 @@ mod tests {
const DEFAULT_DISPATCH_WEIGHT: Weight = 1;
const DEFAULT_SIZE: u32 = 1;
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST
+ BASE_MESSAGE_DELIVERY_TRANSACTION_COST
+ DEFAULT_DISPATCH_WEIGHT
+ (DEFAULT_SIZE as TestSourceChainBalance);
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST +
BASE_MESSAGE_DELIVERY_TRANSACTION_COST +
DEFAULT_DISPATCH_WEIGHT +
(DEFAULT_SIZE as TestSourceChainBalance);
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>;
type TestStrategy = MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
type TestStrategy =
MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
fn source_nonces(
new_nonces: RangeInclusive<MessageNonce>,
@@ -863,14 +871,12 @@ mod tests {
strategy: BasicStrategy::new(),
};
race_strategy
.strategy
.source_nonces_updated(header_id(1), source_nonces(20..=23, 19, DEFAULT_REWARD, AtSourceChain));
race_strategy.strategy.source_nonces_updated(
header_id(1),
source_nonces(20..=23, 19, DEFAULT_REWARD, AtSourceChain),
);
let target_nonces = TargetClientNonces {
latest_nonce: 19,
nonces_data: (),
};
let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () };
race_strategy
.strategy
.best_target_nonces_updated(target_nonces.clone(), &mut race_state);
@@ -890,7 +896,9 @@ mod tests {
#[test]
fn weights_map_works_as_nonces_range() {
fn build_map(range: RangeInclusive<MessageNonce>) -> MessageDetailsMap<TestSourceChainBalance> {
fn build_map(
range: RangeInclusive<MessageNonce>,
) -> MessageDetailsMap<TestSourceChainBalance> {
range
.map(|idx| {
(
@@ -937,7 +945,8 @@ mod tests {
// we need to wait until confirmations will be delivered by receiving race
strategy.latest_confirmed_nonces_at_source = vec![(
header_id(1),
strategy.target_nonces.as_ref().unwrap().latest_nonce - strategy.max_unconfirmed_nonces_at_target,
strategy.target_nonces.as_ref().unwrap().latest_nonce -
strategy.max_unconfirmed_nonces_at_target,
)]
.into_iter()
.collect();
@@ -945,13 +954,16 @@ mod tests {
}
#[async_std::test]
async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available() {
async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available()
{
let (state, mut strategy) = prepare_strategy();
// if there are new confirmed nonces on source, we want to relay this information
// to target to prune rewards queue
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce =
prev_confirmed_nonce_at_source - 1;
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=23), proof_parameters(true, 4)))
@@ -965,8 +977,10 @@ mod tests {
// if there are already `max_unrewarded_relayer_entries_at_target` entries at target,
// we need to wait until rewards will be paid
{
let mut unrewarded_relayers = &mut strategy.target_nonces.as_mut().unwrap().nonces_data.unrewarded_relayers;
unrewarded_relayers.unrewarded_relayer_entries = strategy.max_unrewarded_relayer_entries_at_target;
let mut unrewarded_relayers =
&mut strategy.target_nonces.as_mut().unwrap().nonces_data.unrewarded_relayers;
unrewarded_relayers.unrewarded_relayer_entries =
strategy.max_unrewarded_relayer_entries_at_target;
unrewarded_relayers.messages_in_oldest_entry = 4;
}
assert_eq!(strategy.select_nonces_to_deliver(state).await, None);
@@ -979,12 +993,14 @@ mod tests {
// if there are already `max_unrewarded_relayer_entries_at_target` entries at target,
// we need to prove at least `messages_in_oldest_entry` rewards
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
{
let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data;
nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1;
let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers;
unrewarded_relayers.unrewarded_relayer_entries = strategy.max_unrewarded_relayer_entries_at_target;
unrewarded_relayers.unrewarded_relayer_entries =
strategy.max_unrewarded_relayer_entries_at_target;
unrewarded_relayers.messages_in_oldest_entry = 4;
}
assert_eq!(strategy.select_nonces_to_deliver(state).await, None);
@@ -996,12 +1012,14 @@ mod tests {
// if there are already `max_unrewarded_relayer_entries_at_target` entries at target,
// we need to prove at least `messages_in_oldest_entry` rewards
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
{
let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data;
nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 3;
let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers;
unrewarded_relayers.unrewarded_relayer_entries = strategy.max_unrewarded_relayer_entries_at_target;
unrewarded_relayers.unrewarded_relayer_entries =
strategy.max_unrewarded_relayer_entries_at_target;
unrewarded_relayers.messages_in_oldest_entry = 3;
}
assert_eq!(
@@ -1023,15 +1041,13 @@ mod tests {
}
#[async_std::test]
async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_weight() {
async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_weight(
) {
let (state, mut strategy) = prepare_strategy();
// first message doesn't fit in the batch, because it has weight (10) that overflows max weight (4)
strategy.strategy.source_queue_mut()[0]
.1
.get_mut(&20)
.unwrap()
.dispatch_weight = 10;
// first message doesn't fit in the batch, because it has weight (10) that overflows max
// weight (4)
strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().dispatch_weight = 10;
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=20), proof_parameters(false, 10)))
@@ -1051,10 +1067,12 @@ mod tests {
}
#[async_std::test]
async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_size() {
async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_size(
) {
let (state, mut strategy) = prepare_strategy();
// first message doesn't fit in the batch, because it has weight (10) that overflows max weight (4)
// first message doesn't fit in the batch, because it has weight (10) that overflows max
// weight (4)
strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().size = 10;
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
@@ -1066,7 +1084,8 @@ mod tests {
async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_is_upper_limit() {
let (state, mut strategy) = prepare_strategy();
// not all queued messages may fit in the batch, because batch has max number of messages limit
// not all queued messages may fit in the batch, because batch has max number of messages
// limit
strategy.max_messages_in_single_batch = 3;
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
@@ -1075,16 +1094,18 @@ mod tests {
}
#[async_std::test]
async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_are_unconfirmed_nonces() {
async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_are_unconfirmed_nonces(
) {
let (state, mut strategy) = prepare_strategy();
// 1 delivery confirmation from target to source is still missing, so we may only
// relay 3 new messages
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.latest_confirmed_nonces_at_source = vec![(header_id(1), prev_confirmed_nonce_at_source - 1)]
.into_iter()
.collect();
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.latest_confirmed_nonces_at_source =
vec![(header_id(1), prev_confirmed_nonce_at_source - 1)].into_iter().collect();
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce =
prev_confirmed_nonce_at_source - 1;
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=22), proof_parameters(false, 3)))
@@ -1099,30 +1120,35 @@ mod tests {
//
// => so we can't deliver more than 3 messages
let (mut state, mut strategy) = prepare_strategy();
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.latest_confirmed_nonces_at_source = vec![
(header_id(1), prev_confirmed_nonce_at_source - 1),
(header_id(2), prev_confirmed_nonce_at_source),
]
.into_iter()
.collect();
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1;
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce =
prev_confirmed_nonce_at_source - 1;
state.best_finalized_source_header_id_at_best_target = Some(header_id(1));
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=22), proof_parameters(false, 3)))
);
// the same situation, but the header 2 is known to the target node, so we may deliver reward confirmation
// the same situation, but the header 2 is known to the target node, so we may deliver
// reward confirmation
let (mut state, mut strategy) = prepare_strategy();
let prev_confirmed_nonce_at_source = strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
let prev_confirmed_nonce_at_source =
strategy.latest_confirmed_nonces_at_source.back().unwrap().1;
strategy.latest_confirmed_nonces_at_source = vec![
(header_id(1), prev_confirmed_nonce_at_source - 1),
(header_id(2), prev_confirmed_nonce_at_source),
]
.into_iter()
.collect();
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1;
strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce =
prev_confirmed_nonce_at_source - 1;
state.best_finalized_source_header_id_at_source = Some(header_id(2));
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
assert_eq!(
@@ -1136,8 +1162,9 @@ mod tests {
// let's prepare situation when:
// - all messages [20; 23] have been generated at source block#1;
let (mut state, mut strategy) = prepare_strategy();
// - messages [20; 21] have been delivered, but messages [11; 20] can't be delivered because of unrewarded
// relayers vector capacity;
//
// - messages [20; 21] have been delivered, but messages [11; 20] can't be delivered because
// of unrewarded relayers vector capacity;
strategy.max_unconfirmed_nonces_at_target = 2;
assert_eq!(
strategy.select_nonces_to_deliver(state.clone()).await,
@@ -1158,19 +1185,15 @@ mod tests {
&mut state,
);
assert_eq!(strategy.select_nonces_to_deliver(state).await, None);
//
// - messages [1; 10] receiving confirmation has been delivered at source block#2;
strategy.source_nonces_updated(
header_id(2),
SourceClientNonces {
new_nonces: MessageDetailsMap::new(),
confirmed_nonce: Some(21),
},
SourceClientNonces { new_nonces: MessageDetailsMap::new(), confirmed_nonce: Some(21) },
);
//
// - so now we'll need to relay source block#11 to be able to accept messages [11; 20].
assert_eq!(
strategy.required_source_header_at_target(&header_id(1)),
Some(header_id(2))
);
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2)));
}
#[async_std::test]
@@ -1233,8 +1256,8 @@ mod tests {
// so now we have:
// - 20..=23 with reward = cost
// - 24..=24 with reward less than cost, but we're deducting `DEFAULT_DISPATCH_WEIGHT` from the
// cost, so it should be fine;
// - 24..=24 with reward less than cost, but we're deducting `DEFAULT_DISPATCH_WEIGHT`
// from the cost, so it should be fine;
// => when MSG#24 fee is paid at the target chain, strategy shall select all 20..=24
// => when MSG#25 fee is paid at the source chain, strategy shall only select 20..=23
strategy.select_nonces_to_deliver(state).await
@@ -1255,11 +1278,11 @@ mod tests {
// Real scenario that has happened on test deployments:
// 1) relayer witnessed M1 at block 1 => it has separate entry in the `source_queue`
// 2) relayer witnessed M2 at block 2 => it has separate entry in the `source_queue`
// 3) if block 2 is known to the target node, then both M1 and M2 are selected for single delivery,
// even though weight(M1+M2) > larger than largest allowed weight
// 3) if block 2 is known to the target node, then both M1 and M2 are selected for single
// delivery, even though weight(M1+M2) > larger than largest allowed weight
//
// This was happening because selector (`select_nonces_for_delivery_transaction`) has been called
// for every `source_queue` entry separately without preserving any context.
// This was happening because selector (`select_nonces_for_delivery_transaction`) has been
// called for every `source_queue` entry separately without preserving any context.
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD, AtSourceChain);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
@@ -54,10 +54,12 @@ pub trait MessageRace {
}
/// State of race source client.
type SourceClientState<P> = ClientState<<P as MessageRace>::SourceHeaderId, <P as MessageRace>::TargetHeaderId>;
type SourceClientState<P> =
ClientState<<P as MessageRace>::SourceHeaderId, <P as MessageRace>::TargetHeaderId>;
/// State of race target client.
type TargetClientState<P> = ClientState<<P as MessageRace>::TargetHeaderId, <P as MessageRace>::SourceHeaderId>;
type TargetClientState<P> =
ClientState<<P as MessageRace>::TargetHeaderId, <P as MessageRace>::SourceHeaderId>;
/// Inclusive nonces range.
pub trait NoncesRange: Debug + Sized {
@@ -155,7 +157,10 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
/// Should return true if nothing has to be synced.
fn is_empty(&self) -> bool;
/// Return id of source header that is required to be on target to continue synchronization.
fn required_source_header_at_target(&self, current_best: &SourceHeaderId) -> Option<SourceHeaderId>;
fn required_source_header_at_target(
&self,
current_best: &SourceHeaderId,
) -> Option<SourceHeaderId>;
/// Return the best nonce at source node.
///
/// `Some` is returned only if we are sure that the value is greater or equal
@@ -167,7 +172,11 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
fn best_at_target(&self) -> Option<MessageNonce>;
/// Called when nonces are updated at source node of the race.
fn source_nonces_updated(&mut self, at_block: SourceHeaderId, nonces: SourceClientNonces<Self::SourceNoncesRange>);
fn source_nonces_updated(
&mut self,
at_block: SourceHeaderId,
nonces: SourceClientNonces<Self::SourceNoncesRange>,
);
/// Called when best nonces are updated at target node of the race.
fn best_target_nonces_updated(
&mut self,
@@ -430,8 +439,10 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
strategy,
);
return Err(FailedClient::Both);
} else if race_state.nonces_to_submit.is_none() && race_state.nonces_submitted.is_none() && strategy.is_empty()
return Err(FailedClient::Both)
} else if race_state.nonces_to_submit.is_none() &&
race_state.nonces_submitted.is_none() &&
strategy.is_empty()
{
stall_countdown = Instant::now();
}
@@ -439,7 +450,8 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
if source_client_is_online {
source_client_is_online = false;
let nonces_to_deliver = select_nonces_to_deliver(race_state.clone(), &mut strategy).await;
let nonces_to_deliver =
select_nonces_to_deliver(race_state.clone(), &mut strategy).await;
let best_at_source = strategy.best_at_source();
if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver {
@@ -451,9 +463,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
at_block,
);
source_generate_proof.set(
race_source
.generate_proof(at_block, nonces_range, proof_parameters)
.fuse(),
race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(),
);
} else if source_nonces_required && best_at_source.is_some() {
log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name());
@@ -516,7 +526,9 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
}
}
impl<SourceHeaderId, TargetHeaderId, Proof> Default for RaceState<SourceHeaderId, TargetHeaderId, Proof> {
impl<SourceHeaderId, TargetHeaderId, Proof> Default
for RaceState<SourceHeaderId, TargetHeaderId, Proof>
{
fn default() -> Self {
RaceState {
best_finalized_source_header_id_at_source: None,
@@ -539,7 +551,7 @@ where
let need_update = now_time.saturating_duration_since(prev_time) > Duration::from_secs(10);
if !need_update {
return prev_time;
return prev_time
}
let now_best_nonce_at_source = strategy.best_at_source();
@@ -569,11 +581,7 @@ where
.select_nonces_to_deliver(race_state)
.await
.map(|(nonces_range, proof_parameters)| {
(
best_finalized_source_header_id_at_best_target,
nonces_range,
proof_parameters,
)
(best_finalized_source_header_id_at_best_target, nonces_range, proof_parameters)
})
}
@@ -592,8 +600,14 @@ mod tests {
// target node only knows about source' BEST_AT_TARGET block
// source node has BEST_AT_SOURCE > BEST_AT_TARGET block
let mut race_state = RaceState::<_, _, ()> {
best_finalized_source_header_id_at_source: Some(HeaderId(BEST_AT_SOURCE, BEST_AT_SOURCE)),
best_finalized_source_header_id_at_best_target: Some(HeaderId(BEST_AT_TARGET, BEST_AT_TARGET)),
best_finalized_source_header_id_at_source: Some(HeaderId(
BEST_AT_SOURCE,
BEST_AT_SOURCE,
)),
best_finalized_source_header_id_at_best_target: Some(HeaderId(
BEST_AT_TARGET,
BEST_AT_TARGET,
)),
best_target_header_id: Some(HeaderId(0, 0)),
best_finalized_target_header_id: Some(HeaderId(0, 0)),
nonces_to_submit: None,
@@ -604,16 +618,10 @@ mod tests {
let mut strategy = BasicStrategy::new();
strategy.source_nonces_updated(
HeaderId(GENERATED_AT, GENERATED_AT),
SourceClientNonces {
new_nonces: 0..=10,
confirmed_nonce: None,
},
SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None },
);
strategy.best_target_nonces_updated(
TargetClientNonces {
latest_nonce: 5u64,
nonces_data: (),
},
TargetClientNonces { latest_nonce: 5u64, nonces_data: () },
&mut race_state,
);
@@ -13,16 +13,19 @@
//! Message receiving race delivers proof-of-messages-delivery from "lane.target" to "lane.source".
use crate::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf};
use crate::message_lane_loop::{
SourceClient as MessageLaneSourceClient, SourceClientState, TargetClient as MessageLaneTargetClient,
TargetClientState,
use crate::{
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{
SourceClient as MessageLaneSourceClient, SourceClientState,
TargetClient as MessageLaneTargetClient, TargetClientState,
},
message_race_loop::{
MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient,
TargetClientNonces,
},
message_race_strategy::BasicStrategy,
metrics::MessageLaneLoopMetrics,
};
use crate::message_race_loop::{
MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient, TargetClientNonces,
};
use crate::message_race_strategy::BasicStrategy;
use crate::metrics::MessageLaneLoopMetrics;
use async_trait::async_trait;
use bp_messages::MessageNonce;
@@ -129,11 +132,7 @@ where
nonces: RangeInclusive<MessageNonce>,
_proof_parameters: Self::ProofParameters,
) -> Result<
(
TargetHeaderIdOf<P>,
RangeInclusive<MessageNonce>,
P::MessagesReceivingProof,
),
(TargetHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesReceivingProof),
Self::Error,
> {
self.client
@@ -168,19 +167,14 @@ where
at_block: SourceHeaderIdOf<P>,
update_metrics: bool,
) -> Result<(SourceHeaderIdOf<P>, TargetClientNonces<()>), Self::Error> {
let (at_block, latest_confirmed_nonce) = self.client.latest_confirmed_received_nonce(at_block).await?;
let (at_block, latest_confirmed_nonce) =
self.client.latest_confirmed_received_nonce(at_block).await?;
if update_metrics {
if let Some(metrics_msg) = self.metrics_msg.as_ref() {
metrics_msg.update_source_latest_confirmed_nonce::<P>(latest_confirmed_nonce);
}
}
Ok((
at_block,
TargetClientNonces {
latest_nonce: latest_confirmed_nonce,
nonces_data: (),
},
))
Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () }))
}
async fn submit_proof(
@@ -189,9 +183,7 @@ where
nonces: RangeInclusive<MessageNonce>,
proof: P::MessagesReceivingProof,
) -> Result<RangeInclusive<MessageNonce>, Self::Error> {
self.client
.submit_messages_receiving_proof(generated_at_block, proof)
.await?;
self.client.submit_messages_receiving_proof(generated_at_block, proof).await?;
Ok(nonces)
}
}
@@ -17,7 +17,9 @@
//! 2) new nonces may be proved to target node (i.e. they have appeared at the
//! block, which is known to the target node).
use crate::message_race_loop::{NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces};
use crate::message_race_loop::{
NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces,
};
use async_trait::async_trait;
use bp_messages::MessageNonce;
@@ -40,15 +42,29 @@ pub struct BasicStrategy<
> {
/// All queued nonces.
source_queue: SourceRangesQueue<SourceHeaderHash, SourceHeaderNumber, SourceNoncesRange>,
/// The best nonce known to target node (at its best block). `None` if it has not been received yet.
/// The best nonce known to target node (at its best block). `None` if it has not been received
/// yet.
best_target_nonce: Option<MessageNonce>,
/// Unused generic types dump.
_phantom: PhantomData<(TargetHeaderNumber, TargetHeaderHash, Proof)>,
}
impl<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
BasicStrategy<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
where
impl<
SourceHeaderNumber,
SourceHeaderHash,
TargetHeaderNumber,
TargetHeaderHash,
SourceNoncesRange,
Proof,
>
BasicStrategy<
SourceHeaderNumber,
SourceHeaderHash,
TargetHeaderNumber,
TargetHeaderHash,
SourceNoncesRange,
Proof,
> where
SourceHeaderHash: Clone,
SourceHeaderNumber: Clone + Ord,
SourceNoncesRange: NoncesRange,
@@ -79,9 +95,9 @@ where
/// Returns index of the latest source queue entry, that may be delivered to the target node.
///
/// Returns `None` if no entries may be delivered. All entries before and including the `Some(_)`
/// index are guaranteed to be witnessed at source blocks that are known to be finalized at the
/// target node.
/// Returns `None` if no entries may be delivered. All entries before and including the
/// `Some(_)` index are guaranteed to be witnessed at source blocks that are known to be
/// finalized at the target node.
pub fn maximal_available_source_queue_index(
&self,
race_state: RaceState<
@@ -95,12 +111,12 @@ where
// if we have already selected nonces that we want to submit, do nothing
if race_state.nonces_to_submit.is_some() {
return None;
return None
}
// if we already submitted some nonces, do nothing
if race_state.nonces_submitted.is_some() {
return None;
return None
}
// 1) we want to deliver all nonces, starting from `target_nonce + 1`
@@ -124,17 +140,34 @@ where
while let Some((queued_at, queued_range)) = self.source_queue.pop_front() {
if let Some(range_to_requeue) = queued_range.greater_than(nonce) {
self.source_queue.push_front((queued_at, range_to_requeue));
break;
break
}
}
}
}
#[async_trait]
impl<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
RaceStrategy<HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>, Proof>
for BasicStrategy<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
where
impl<
SourceHeaderNumber,
SourceHeaderHash,
TargetHeaderNumber,
TargetHeaderHash,
SourceNoncesRange,
Proof,
>
RaceStrategy<
HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>,
Proof,
>
for BasicStrategy<
SourceHeaderNumber,
SourceHeaderHash,
TargetHeaderNumber,
TargetHeaderHash,
SourceNoncesRange,
Proof,
> where
SourceHeaderHash: Clone + Debug + Send,
SourceHeaderNumber: Clone + Ord + Debug + Send,
SourceNoncesRange: NoncesRange + Debug + Send,
@@ -162,7 +195,8 @@ where
fn best_at_source(&self) -> Option<MessageNonce> {
let best_in_queue = self.source_queue.back().map(|(_, range)| range.end());
match (best_in_queue, self.best_target_nonce) {
(Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce => Some(best_in_queue),
(Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce =>
Some(best_in_queue),
(_, Some(best_target_nonce)) => Some(best_target_nonce),
(_, None) => None,
}
@@ -205,18 +239,17 @@ where
if let Some(best_target_nonce) = self.best_target_nonce {
if nonce < best_target_nonce {
return;
return
}
}
while let Some(true) = self.source_queue.front().map(|(_, range)| range.begin() <= nonce) {
let maybe_subrange = self
.source_queue
.pop_front()
.and_then(|(at_block, range)| range.greater_than(nonce).map(|subrange| (at_block, subrange)));
let maybe_subrange = self.source_queue.pop_front().and_then(|(at_block, range)| {
range.greater_than(nonce).map(|subrange| (at_block, subrange))
});
if let Some((at_block, subrange)) = maybe_subrange {
self.source_queue.push_front((at_block, subrange));
break;
break
}
}
@@ -238,10 +271,8 @@ where
race_state.nonces_submitted = None;
}
self.best_target_nonce = Some(std::cmp::max(
self.best_target_nonce.unwrap_or(nonces.latest_nonce),
nonce,
));
self.best_target_nonce =
Some(std::cmp::max(self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonce));
}
fn finalized_target_nonces_updated(
@@ -278,9 +309,12 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::message_lane::MessageLane;
use crate::message_lane_loop::tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, TestSourceHeaderNumber,
use crate::{
message_lane::MessageLane,
message_lane_loop::tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash,
TestSourceHeaderNumber,
},
};
type SourceNoncesRange = RangeInclusive<MessageNonce>;
@@ -295,17 +329,11 @@ mod tests {
>;
fn source_nonces(new_nonces: SourceNoncesRange) -> SourceClientNonces<SourceNoncesRange> {
SourceClientNonces {
new_nonces,
confirmed_nonce: None,
}
SourceClientNonces { new_nonces, confirmed_nonce: None }
}
fn target_nonces(latest_nonce: MessageNonce) -> TargetClientNonces<()> {
TargetClientNonces {
latest_nonce,
nonces_data: (),
}
TargetClientNonces { latest_nonce, nonces_data: () }
}
#[test]
@@ -420,18 +448,12 @@ mod tests {
strategy.source_nonces_updated(header_id(5), source_nonces(7..=8));
state.best_finalized_source_header_id_at_best_target = Some(header_id(4));
assert_eq!(
strategy.select_nonces_to_deliver(state.clone()).await,
Some((1..=6, ()))
);
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=6, ())));
strategy.best_target_nonces_updated(target_nonces(6), &mut state);
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
state.best_finalized_source_header_id_at_best_target = Some(header_id(5));
assert_eq!(
strategy.select_nonces_to_deliver(state.clone()).await,
Some((7..=8, ()))
);
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((7..=8, ())));
strategy.best_target_nonces_updated(target_nonces(8), &mut state);
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
}
@@ -471,16 +493,17 @@ mod tests {
strategy.source_nonces_updated(header_id(3), source_nonces(7..=9));
fn source_queue_nonces(
source_queue: &SourceRangesQueue<TestSourceHeaderHash, TestSourceHeaderNumber, SourceNoncesRange>,
source_queue: &SourceRangesQueue<
TestSourceHeaderHash,
TestSourceHeaderNumber,
SourceNoncesRange,
>,
) -> Vec<MessageNonce> {
source_queue.iter().flat_map(|(_, range)| range.clone()).collect()
}
strategy.remove_le_nonces_from_source_queue(1);
assert_eq!(
source_queue_nonces(&strategy.source_queue),
vec![2, 3, 4, 5, 6, 7, 8, 9],
);
assert_eq!(source_queue_nonces(&strategy.source_queue), vec![2, 3, 4, 5, 6, 7, 8, 9],);
strategy.remove_le_nonces_from_source_queue(5);
assert_eq!(source_queue_nonces(&strategy.source_queue), vec![6, 7, 8, 9],);
+20 -6
View File
@@ -16,8 +16,10 @@
//! Metrics for message lane relay loop.
use crate::message_lane::MessageLane;
use crate::message_lane_loop::{SourceClientState, TargetClientState};
use crate::{
message_lane::MessageLane,
message_lane_loop::{SourceClientState, TargetClientState},
};
use bp_messages::MessageNonce;
use relay_utils::metrics::{metric_name, register, GaugeVec, Opts, PrometheusError, Registry, U64};
@@ -81,28 +83,40 @@ impl MessageLaneLoopMetrics {
}
/// Update latest generated nonce at source.
pub fn update_source_latest_generated_nonce<P: MessageLane>(&self, source_latest_generated_nonce: MessageNonce) {
pub fn update_source_latest_generated_nonce<P: MessageLane>(
&self,
source_latest_generated_nonce: MessageNonce,
) {
self.lane_state_nonces
.with_label_values(&["source_latest_generated"])
.set(source_latest_generated_nonce);
}
/// Update the latest confirmed nonce at source.
pub fn update_source_latest_confirmed_nonce<P: MessageLane>(&self, source_latest_confirmed_nonce: MessageNonce) {
pub fn update_source_latest_confirmed_nonce<P: MessageLane>(
&self,
source_latest_confirmed_nonce: MessageNonce,
) {
self.lane_state_nonces
.with_label_values(&["source_latest_confirmed"])
.set(source_latest_confirmed_nonce);
}
/// Update the latest received nonce at target.
pub fn update_target_latest_received_nonce<P: MessageLane>(&self, target_latest_generated_nonce: MessageNonce) {
pub fn update_target_latest_received_nonce<P: MessageLane>(
&self,
target_latest_generated_nonce: MessageNonce,
) {
self.lane_state_nonces
.with_label_values(&["target_latest_received"])
.set(target_latest_generated_nonce);
}
/// Update the latest confirmed nonce at target.
pub fn update_target_latest_confirmed_nonce<P: MessageLane>(&self, target_latest_confirmed_nonce: MessageNonce) {
pub fn update_target_latest_confirmed_nonce<P: MessageLane>(
&self,
target_latest_confirmed_nonce: MessageNonce,
) {
self.lane_state_nonces
.with_label_values(&["target_latest_confirmed"])
.set(target_latest_confirmed_nonce);
+5 -10
View File
@@ -62,14 +62,7 @@ pub fn initialize_logger(with_timestamp: bool) {
let log_level = color_level(record.level());
let log_target = color_target(record.target());
writeln!(
buf,
"{}{} {} {}",
loop_name_prefix(),
log_level,
log_target,
record.args(),
)
writeln!(buf, "{}{} {} {}", loop_name_prefix(), log_level, log_target, record.args(),)
});
}
@@ -81,12 +74,14 @@ pub(crate) fn initialize_loop(loop_name: String) {
LOOP_NAME.with(|g_loop_name| *g_loop_name.borrow_mut() = loop_name);
}
/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop` call.
/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop`
/// call.
fn loop_name_prefix() -> String {
// try_with to avoid panic outside of async-std task context
LOOP_NAME
.try_with(|loop_name| {
// using borrow is ok here, because loop is only initialized once (=> borrow_mut will only be called once)
// using borrow is ok here, because loop is only initialized once (=> borrow_mut will
// only be called once)
let loop_name = loop_name.borrow();
if loop_name.is_empty() {
String::new()
+9 -6
View File
@@ -168,12 +168,12 @@ pub fn format_ids<Id: std::fmt::Debug>(mut ids: impl ExactSizeIterator<Item = Id
let id0 = ids.next().expect(NTH_PROOF);
let id1 = ids.next().expect(NTH_PROOF);
format!("[{:?}, {:?}]", id0, id1)
}
},
len => {
let id0 = ids.next().expect(NTH_PROOF);
let id_last = ids.last().expect(NTH_PROOF);
format!("{}:[{:?} ... {:?}]", len, id0, id_last)
}
},
}
}
@@ -220,7 +220,10 @@ impl ProcessFutureResult {
/// Returns Ok(true) if future has succeeded.
/// Returns Ok(false) if future has failed with non-connection error.
/// Returns Err if future is `ConnectionFailed`.
pub fn fail_if_connection_error(self, failed_client: FailedClient) -> Result<bool, FailedClient> {
pub fn fail_if_connection_error(
self,
failed_client: FailedClient,
) -> Result<bool, FailedClient> {
match self {
ProcessFutureResult::Success => Ok(true),
ProcessFutureResult::Failed => Ok(false),
@@ -247,7 +250,7 @@ where
on_success(result);
retry_backoff.reset();
ProcessFutureResult::Success
}
},
Err(error) if error.is_connection_error() => {
log::error!(
target: "bridge",
@@ -259,7 +262,7 @@ where
retry_backoff.reset();
go_offline_future.set(go_offline(CONNECTION_ERROR_DELAY).fuse());
ProcessFutureResult::ConnectionFailed
}
},
Err(error) => {
let retry_delay = retry_backoff.next_backoff().unwrap_or(CONNECTION_ERROR_DELAY);
log::error!(
@@ -272,6 +275,6 @@ where
go_offline_future.set(go_offline(retry_delay).fuse());
ProcessFutureResult::Failed
}
},
}
}
+10 -18
View File
@@ -82,21 +82,14 @@ pub trait StandaloneMetrics: Metrics {
impl Default for MetricsAddress {
fn default() -> Self {
MetricsAddress {
host: "127.0.0.1".into(),
port: 9616,
}
MetricsAddress { host: "127.0.0.1".into(), port: 9616 }
}
}
impl MetricsParams {
/// Creates metrics params so that metrics are not exposed.
pub fn disabled() -> Self {
MetricsParams {
address: None,
registry: None,
metrics_prefix: None,
}
MetricsParams { address: None, registry: None, metrics_prefix: None }
}
/// Do not expose metrics.
@@ -114,11 +107,7 @@ impl MetricsParams {
impl From<Option<MetricsAddress>> for MetricsParams {
fn from(address: Option<MetricsAddress>) -> Self {
MetricsParams {
address,
registry: None,
metrics_prefix: None,
}
MetricsParams { address, registry: None, metrics_prefix: None }
}
}
@@ -134,7 +123,10 @@ pub fn metric_name(prefix: Option<&str>, name: &str) -> String {
/// Set value of gauge metric.
///
/// If value is `Ok(None)` or `Err(_)`, metric would have default value.
pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(gauge: &Gauge<V>, value: Result<Option<T>, E>) {
pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(
gauge: &Gauge<V>,
value: Result<Option<T>, E>,
) {
gauge.set(match value {
Ok(Some(value)) => {
log::trace!(
@@ -144,7 +136,7 @@ pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(gauge: &G
value,
);
value
}
},
Ok(None) => {
log::warn!(
target: "bridge-metrics",
@@ -152,7 +144,7 @@ pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(gauge: &G
gauge.desc().first().map(|d| &d.fq_name),
);
Default::default()
}
},
Err(error) => {
log::warn!(
target: "bridge-metrics",
@@ -161,6 +153,6 @@ pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(gauge: &G
error,
);
Default::default()
}
},
})
}
@@ -14,7 +14,9 @@
// 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::metrics::{metric_name, register, F64SharedRef, Gauge, PrometheusError, Registry, StandaloneMetrics, F64};
use crate::metrics::{
metric_name, register, F64SharedRef, Gauge, PrometheusError, Registry, StandaloneMetrics, F64,
};
use async_std::sync::{Arc, RwLock};
use async_trait::async_trait;
@@ -100,18 +102,12 @@ impl StandaloneMetrics for FloatJsonValueMetric {
/// Parse HTTP service response.
fn parse_service_response(json_path: &str, response: &str) -> Result<f64, String> {
let json = serde_json::from_str(response).map_err(|err| {
format!(
"Failed to parse HTTP service response: {:?}. Response: {:?}",
err, response,
)
format!("Failed to parse HTTP service response: {:?}. Response: {:?}", err, response,)
})?;
let mut selector = jsonpath_lib::selector(&json);
let maybe_selected_value = selector(json_path).map_err(|err| {
format!(
"Failed to select value from response: {:?}. Response: {:?}",
err, response,
)
format!("Failed to select value from response: {:?}. Response: {:?}", err, response,)
})?;
let selected_value = maybe_selected_value
.first()
@@ -121,7 +117,7 @@ fn parse_service_response(json_path: &str, response: &str) -> Result<f64, String
return Err(format!(
"Failed to parse float value {:?} from response. It is assumed to be positive and normal",
selected_value,
));
))
}
Ok(selected_value)
+13 -6
View File
@@ -17,7 +17,8 @@
//! Global system-wide Prometheus metrics exposed by relays.
use crate::metrics::{
metric_name, register, Gauge, GaugeVec, Opts, PrometheusError, Registry, StandaloneMetrics, F64, U64,
metric_name, register, Gauge, GaugeVec, Opts, PrometheusError, Registry, StandaloneMetrics,
F64, U64,
};
use async_std::sync::{Arc, Mutex};
@@ -50,7 +51,10 @@ impl GlobalMetrics {
registry,
)?,
process_cpu_usage_percentage: register(
Gauge::new(metric_name(prefix, "process_cpu_usage_percentage"), "Process CPU usage")?,
Gauge::new(
metric_name(prefix, "process_cpu_usage_percentage"),
"Process CPU usage",
)?,
registry,
)?,
process_memory_usage_bytes: register(
@@ -92,16 +96,19 @@ impl StandaloneMetrics for GlobalMetrics {
memory_usage,
);
self.process_cpu_usage_percentage
.set(if cpu_usage.is_finite() { cpu_usage } else { 0f64 });
self.process_cpu_usage_percentage.set(if cpu_usage.is_finite() {
cpu_usage
} else {
0f64
});
self.process_memory_usage_bytes.set(memory_usage);
}
},
_ => {
log::warn!(
target: "bridge-metrics",
"Failed to refresh process information. Metrics may show obsolete values",
);
}
},
}
}
+28 -25
View File
@@ -14,8 +14,10 @@
// 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::metrics::{Metrics, MetricsAddress, MetricsParams, PrometheusError, StandaloneMetrics};
use crate::{FailedClient, MaybeConnectionError};
use crate::{
metrics::{Metrics, MetricsAddress, MetricsParams, PrometheusError, StandaloneMetrics},
FailedClient, MaybeConnectionError,
};
use async_trait::async_trait;
use std::{fmt::Debug, future::Future, net::SocketAddr, time::Duration};
@@ -45,15 +47,11 @@ impl Client for () {
/// Returns generic loop that may be customized and started.
pub fn relay_loop<SC, TC>(source_client: SC, target_client: TC) -> Loop<SC, TC, ()> {
Loop {
reconnect_delay: RECONNECT_DELAY,
source_client,
target_client,
loop_metric: None,
}
Loop { reconnect_delay: RECONNECT_DELAY, source_client, target_client, loop_metric: None }
}
/// Returns generic relay loop metrics that may be customized and used in one or several relay loops.
/// Returns generic relay loop metrics that may be customized and used in one or several relay
/// loops.
pub fn relay_metrics(prefix: Option<String>, params: MetricsParams) -> LoopMetrics<(), (), ()> {
LoopMetrics {
relay_loop: Loop {
@@ -94,7 +92,11 @@ impl<SC, TC, LM> Loop<SC, TC, LM> {
}
/// Start building loop metrics using given prefix.
pub fn with_metrics(self, prefix: Option<String>, params: MetricsParams) -> LoopMetrics<SC, TC, ()> {
pub fn with_metrics(
self,
prefix: Option<String>,
params: MetricsParams,
) -> LoopMetrics<SC, TC, ()> {
LoopMetrics {
relay_loop: Loop {
reconnect_delay: self.reconnect_delay,
@@ -111,8 +113,8 @@ impl<SC, TC, LM> Loop<SC, TC, LM> {
/// Run relay loop.
///
/// This function represents an outer loop, which in turn calls provided `run_loop` function to do
/// actual job. When `run_loop` returns, this outer loop reconnects to failed client (source,
/// This function represents an outer loop, which in turn calls provided `run_loop` function to
/// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source,
/// target or both) and calls `run_loop` again.
pub async fn run<R, F>(mut self, loop_name: String, run_loop: R) -> anyhow::Result<()>
where
@@ -127,20 +129,20 @@ impl<SC, TC, LM> Loop<SC, TC, LM> {
loop {
let loop_metric = self.loop_metric.clone();
let future_result = run_loop(self.source_client.clone(), self.target_client.clone(), loop_metric);
let future_result =
run_loop(self.source_client.clone(), self.target_client.clone(), loop_metric);
let result = future_result.await;
match result {
Ok(()) => break,
Err(failed_client) => {
Err(failed_client) =>
reconnect_failed_client(
failed_client,
self.reconnect_delay,
&mut self.source_client,
&mut self.target_client,
)
.await
}
.await,
}
log::debug!(target: "bridge", "Restarting relay loop");
@@ -177,8 +179,8 @@ impl<SC, TC, LM> LoopMetrics<SC, TC, LM> {
self,
create_metric: impl FnOnce(&Registry, Option<&str>) -> Result<M, PrometheusError>,
) -> anyhow::Result<Self> {
// since standalone metrics are updating themselves, we may just ignore the fact that the same
// standalone metric is exposed by several loops && only spawn single metric
// since standalone metrics are updating themselves, we may just ignore the fact that the
// same standalone metric is exposed by several loops && only spawn single metric
match create_metric(&self.registry, self.metrics_prefix.as_deref()) {
Ok(standalone_metrics) => standalone_metrics.spawn(),
Err(PrometheusError::AlreadyReg) => (),
@@ -252,8 +254,8 @@ pub async fn reconnect_failed_client(
reconnect_delay.as_secs(),
error,
);
continue;
}
continue
},
}
}
if failed_client == FailedClient::Both || failed_client == FailedClient::Target {
@@ -266,12 +268,12 @@ pub async fn reconnect_failed_client(
reconnect_delay.as_secs(),
error,
);
continue;
}
continue
},
}
}
break;
break
}
}
@@ -280,8 +282,9 @@ fn create_metrics_registry(prefix: Option<String>) -> Registry {
match prefix {
Some(prefix) => {
assert!(!prefix.is_empty(), "Metrics prefix can not be empty");
Registry::new_custom(Some(prefix), None).expect("only fails if prefix is empty; prefix is not empty; qed")
}
Registry::new_custom(Some(prefix), None)
.expect("only fails if prefix is empty; prefix is not empty; qed")
},
None => Registry::new(),
}
}