,
tx_maker: M,
lane: LaneId,
instance: InstanceId,
_marker: PhantomData,
}
/// Substrate transactions maker.
#[async_trait]
pub trait SubstrateTransactionMaker: Clone + Send + Sync {
/// Signed transaction type.
type SignedTransaction: Send + Sync + Encode;
/// Make messages receiving proof transaction.
async fn make_messages_receiving_proof_transaction(
&self,
generated_at_block: TargetHeaderIdOf,
proof: P::MessagesReceivingProof,
) -> Result;
}
impl SubstrateMessagesSource {
/// Create new Substrate headers source.
pub fn new(client: Client, tx_maker: M, lane: LaneId, instance: InstanceId) -> Self {
SubstrateMessagesSource {
client,
tx_maker,
lane,
instance,
_marker: Default::default(),
}
}
}
impl Clone for SubstrateMessagesSource {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
tx_maker: self.tx_maker.clone(),
lane: self.lane,
instance: self.instance,
_marker: Default::default(),
}
}
}
#[async_trait]
impl SourceClient for SubstrateMessagesSource
where
C: Chain,
C::Header: DeserializeOwned,
C::Index: DeserializeOwned,
::Number: Into,
P: MessageLane<
MessagesProof = SubstrateMessagesProof,
SourceHeaderNumber = ::Number,
SourceHeaderHash = ::Hash,
>,
P::TargetHeaderNumber: Decode,
P::TargetHeaderHash: Decode,
M: SubstrateTransactionMaker,
{
type Error = SubstrateError;
async fn reconnect(mut self) -> Result {
let new_client = self.client.clone().reconnect().await?;
self.client = new_client;
Ok(self)
}
async fn state(&self) -> Result, Self::Error> {
read_client_state::<_, P::TargetHeaderHash, P::TargetHeaderNumber>(&self.client, P::TARGET_NAME).await
}
async fn latest_generated_nonce(
&self,
id: SourceHeaderIdOf,
) -> Result<(SourceHeaderIdOf
, MessageNonce), Self::Error> {
let encoded_response = self
.client
.state_call(
// TODO: https://github.com/paritytech/parity-bridges-common/issues/457
"OutboundLaneApi_latest_generated_nonce".into(),
Bytes(self.lane.encode()),
Some(id.1),
)
.await?;
let latest_generated_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_generated_nonce))
}
async fn latest_confirmed_received_nonce(
&self,
id: SourceHeaderIdOf
,
) -> Result<(SourceHeaderIdOf
, MessageNonce), Self::Error> {
let encoded_response = self
.client
.state_call(
// TODO: https://github.com/paritytech/parity-bridges-common/issues/457
"OutboundLaneApi_latest_received_nonce".into(),
Bytes(self.lane.encode()),
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
async fn generated_messages_weights(
&self,
id: SourceHeaderIdOf
,
nonces: RangeInclusive,
) -> Result {
let encoded_response = self
.client
.state_call(
// TODO: https://github.com/paritytech/parity-bridges-common/issues/457
"OutboundLaneApi_messages_dispatch_weight".into(),
Bytes((self.lane, nonces.start(), nonces.end()).encode()),
Some(id.1),
)
.await?;
let weights: Vec<(MessageNonce, Weight)> =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
let mut expected_nonce = *nonces.start();
let mut weights_map = MessageWeightsMap::new();
for (nonce, weight) in weights {
if nonce != expected_nonce {
return Err(SubstrateError::Custom(format!(
"Unexpected nonce in messages_dispatch_weight call result. Expected {}, got {}",
expected_nonce, nonce
)));
}
weights_map.insert(nonce, weight);
expected_nonce += 1;
}
Ok(weights_map)
}
async fn prove_messages(
&self,
id: SourceHeaderIdOf,
nonces: RangeInclusive,
proof_parameters: MessageProofParameters,
) -> Result<(SourceHeaderIdOf, RangeInclusive, P::MessagesProof), Self::Error> {
let proof = self
.client
.prove_messages(
self.instance,
self.lane,
nonces.clone(),
proof_parameters.outbound_state_proof_required,
id.1,
)
.await?;
let proof = (id.1, proof, self.lane, *nonces.start(), *nonces.end());
Ok((id, nonces, (proof_parameters.dispatch_weight, proof)))
}
async fn submit_messages_receiving_proof(
&self,
generated_at_block: TargetHeaderIdOf,
proof: P::MessagesReceivingProof,
) -> Result<(), Self::Error> {
let tx = self
.tx_maker
.make_messages_receiving_proof_transaction(generated_at_block, proof)
.await?;
self.client.submit_extrinsic(Bytes(tx.encode())).await?;
Ok(())
}
}
pub async fn read_client_state(
self_client: &Client,
bridged_chain_name: &str,
) -> Result, HeaderId>, SubstrateError>
where
SelfChain: Chain,
SelfChain::Header: DeserializeOwned,
SelfChain::Index: DeserializeOwned,
BridgedHeaderHash: Decode,
BridgedHeaderNumber: Decode,
{
// 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);
// now let's read id of best finalized peer header at our best finalized block
let best_finalized_peer_on_self_method = format!("{}HeaderApi_finalized_block", bridged_chain_name);
let encoded_best_finalized_peer_on_self = self_client
.state_call(
best_finalized_peer_on_self_method,
Bytes(Vec::new()),
Some(self_best_finalized_header_hash),
)
.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,
);
Ok(ClientState {
best_self: self_best_finalized_id,
best_peer: peer_on_self_best_finalized_id,
})
}