Skip to content

Commit

Permalink
A0-4251: Avoid resumbissions on Aleph via better handling loop (#177)
Browse files Browse the repository at this point in the history
* Wip - better resubmission loop on aleph

* Better resubmission on aleph

* Simplify Azero MostInstance::receive_request

* Update e2e test

* Bump aleph-client version

* Aleph client on release 13 branch

* Update locks

* Use client from release-13 branch
  • Loading branch information
ggawryal authored Apr 29, 2024
1 parent a4565fd commit 04cf4cc
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 87 deletions.
4 changes: 2 additions & 2 deletions e2e-tests/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
aleph_client = { git = "https://github.com/Cardinal-Cryptography/aleph-node/", rev = "d52ede9" }
aleph_client = { git = "https://github.com/Cardinal-Cryptography/aleph-node/", rev = "378efabbf831b213fd0ca2838cd9e8b11fbdd013" }
anyhow = "1.0.80"
env_logger = "0.11.2"
ethers = "2.0.13"
Expand Down
17 changes: 13 additions & 4 deletions e2e-tests/src/test/azero_to_eth.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::str::FromStr;

use aleph_client::{contract::ContractInstance, keypair_from_string, sp_runtime::AccountId32};
use aleph_client::{
contract::{ContractInstance, ExecCallParams},
keypair_from_string,
sp_runtime::AccountId32,
};
use ethers::{
middleware::Middleware,
signers::{coins_bip39::English, MnemonicBuilder, Signer},
Expand Down Expand Up @@ -44,7 +48,12 @@ pub async fn azero_to_eth() -> anyhow::Result<()> {
let approve_args = [most_address.to_string(), transfer_amount.to_string()];

let approve_info = weth_azero
.contract_exec(&azero_signed_connection, "PSP22::approve", &approve_args)
.exec(
&azero_signed_connection,
"PSP22::approve",
&approve_args,
Default::default(),
)
.await?;
info!("`approve` tx info: {:?}", approve_info);

Expand Down Expand Up @@ -73,11 +82,11 @@ pub async fn azero_to_eth() -> anyhow::Result<()> {
];

let send_request_info = most
.contract_exec_value(
.exec(
&azero_signed_connection,
"send_request",
&send_request_args,
1_000_000_000_000_000,
ExecCallParams::new().value(10_000_000_000_000_000),
)
.await?;
info!("`send_request` tx info: {:?}", send_request_info);
Expand Down
6 changes: 4 additions & 2 deletions e2e-tests/src/test/eth_to_azero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ pub async fn eth_to_azero() -> anyhow::Result<()> {
let azero_account = azero_account_keypair.account_id();

let balance_pre_transfer: u128 = weth_azero
.contract_read(
.read(
&azero_connection,
"PSP22::balance_of",
&[azero_account.to_string()],
Default::default(),
)
.await?;
info!(
Expand Down Expand Up @@ -100,10 +101,11 @@ pub async fn eth_to_azero() -> anyhow::Result<()> {

let get_current_balance = || async {
let balance_current: u128 = weth_azero
.contract_read(
.read(
&azero_connection,
"PSP22::balance_of",
&[azero_account.to_string()],
Default::default(),
)
.await?;
Ok(balance_current)
Expand Down
6 changes: 3 additions & 3 deletions relayer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["relayer", "signer_client", "signer", "signer_tester"]
resolver = "2"

[workspace.dependencies]
aleph_client = { git = "https://github.com/Cardinal-Cryptography/aleph-node", rev = "r-13.2" }
aleph_client = { git = "https://github.com/Cardinal-Cryptography/aleph-node", rev = "378efabbf831b213fd0ca2838cd9e8b11fbdd013" }
ethers = { version = "2.0.9" }
subxt = { version = "0.30.1", features = ["substrate-compat"] }
tokio = { version = "1.36" }
Expand Down
77 changes: 25 additions & 52 deletions relayer/relayer/src/contracts/azero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ use std::{
use aleph_client::{
contract::{
event::{translate_events, BlockDetails, ContractEvent},
ContractInstance,
ContractInstance, ExecCallParams, ReadonlyCallParams,
},
contract_transcode::{
ContractMessageTranscoder,
Value::{self, Seq},
},
pallets::contract::{ContractCallArgs, ContractRpc, ContractsUserApi},
sp_weights::weight_v2::Weight,
AccountId, AlephConfig, Connection, SignedConnectionApi, TxInfo, TxStatus,
utility::BlocksApi,
waiting::BlockStatus,
AccountId, AlephConfig, Connection, TxInfo,
};
use log::{error, info, trace};
use subxt::events::Events;
Expand All @@ -38,12 +39,6 @@ pub enum AzeroContractError {

#[error("Missing or invalid field")]
MissingOrInvalidField(String),

#[error("Dry-run reverted")]
DryRunReverted(Result<Value, anyhow::Error>),

#[error("Dispatch error")]
DispatchError(String),
}

pub struct AdvisoryInstance {
Expand All @@ -67,7 +62,7 @@ impl AdvisoryInstance {
) -> Result<(bool, AccountId), AzeroContractError> {
match self
.contract
.contract_read0::<bool, _>(connection, "is_emergency")
.read0::<bool, _>(connection, "is_emergency", Default::default())
.await
{
Ok(is_emergency) => Ok((is_emergency, self.address.clone())),
Expand Down Expand Up @@ -125,44 +120,12 @@ impl MostInstance {
bytes32_to_str(&dest_receiver_address),
request_nonce.to_string(),
];
let call_data = self.transcoder.encode("receive_request", args)?;

let dry_run_args = ContractCallArgs {
origin: signed_connection.account_id().clone(),
dest: self.address.clone(),
value: 0,
gas_limit: Some(gas_limit.clone()),
storage_deposit_limit: None,
input_data: call_data.clone(),
};

// Dry run to detect potential errors
let dry_run_res = match signed_connection.call_and_get(dry_run_args).await?.result {
Ok(res) => res,
Err(why) => {
error!("Dry run failed: {:?}", why);
return Err(AzeroContractError::DispatchError(format!("{:?}", why)));
}
};
if dry_run_res.did_revert() {
let decoded_value = self
.transcoder
.decode_return("receive_request", &mut dry_run_res.data.as_ref());
let params = ExecCallParams::new().gas_limit(gas_limit);

error!("Dry run reverted: {:?}", decoded_value);

return Err(AzeroContractError::DryRunReverted(decoded_value));
}

let call_result = signed_connection
.call(
self.address.clone(),
0,
gas_limit,
None,
call_data,
TxStatus::Finalized,
)
// Exec does dry run first, so there's no need to repeat it here
let call_result = self
.contract
.exec(signed_connection, "receive_request", &args, params)
.await
.map_err(AzeroContractError::AlephClient);
info!("receive_request: {:?}", call_result);
Expand All @@ -172,27 +135,36 @@ impl MostInstance {
pub async fn is_halted(&self, connection: &Connection) -> Result<bool, AzeroContractError> {
Ok(self
.contract
.contract_read0::<Result<bool, _>, _>(connection, "is_halted")
.read0::<Result<bool, _>, _>(connection, "is_halted", Default::default())
.await??)
}

pub async fn _needs_signature(
pub async fn needs_signature(
&self,
connection: &Connection,
request_hash: [u8; 32],
account: AccountId,
committee_id: u128,
block: BlockStatus,
) -> Result<bool, AzeroContractError> {
let params = match block {
BlockStatus::Best => ReadonlyCallParams::new(),
BlockStatus::Finalized => {
let finalized_hash = connection.get_finalized_block_hash().await?;
ReadonlyCallParams::new().at(finalized_hash)
}
};
Ok(self
.contract
.contract_read(
.read(
connection,
"needs_signature",
&[
bytes32_to_str(&request_hash),
account.to_string(),
committee_id.to_string(),
],
params,
)
.await?)
}
Expand All @@ -203,7 +175,7 @@ impl MostInstance {
) -> Result<u128, AzeroContractError> {
Ok(self
.contract
.contract_read0::<Result<u128, _>, _>(connection, "current_committee_id")
.read0::<Result<u128, _>, _>(connection, "current_committee_id", Default::default())
.await??)
}

Expand All @@ -215,10 +187,11 @@ impl MostInstance {
) -> Result<bool, AzeroContractError> {
Ok(self
.contract
.contract_read(
.read(
connection,
"is_in_committee",
&[committee_id.to_string(), account.to_string()],
Default::default(),
)
.await?)
}
Expand Down
72 changes: 50 additions & 22 deletions relayer/relayer/src/handlers/eth.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::{str::FromStr, sync::Arc};

use aleph_client::{AccountId, AsConnection, SignedConnectionApi};
use aleph_client::{waiting::BlockStatus, AsConnection, SignedConnectionApi};
use ethers::{core::types::H256, utils::keccak256};
use log::{debug, error, info, trace, warn};
use rustc_hex::FromHexError;
use thiserror::Error;
use tokio::{
select,
sync::{broadcast, mpsc},
time::{sleep, Duration},
};

use crate::{
Expand All @@ -16,9 +17,12 @@ use crate::{
contracts::{AzeroContractError, CrosschainTransferRequestFilter, MostEvents, MostInstance},
helpers::concat_u8_arrays,
listeners::EthMostEvents,
CircuitBreakerEvent,
AccountId, CircuitBreakerEvent,
};

// Frequency of checking for finality of the transaction
const AZERO_WAIT_FOR_FINALITY_CHECK: Duration = Duration::from_millis(1000);

#[derive(Debug, Error)]
#[error(transparent)]
#[non_exhaustive]
Expand Down Expand Up @@ -108,8 +112,6 @@ impl EthereumEventHandler {
config.azero_proof_size_limit,
)?;

// send vote

let committee_id = committee_id.as_u128();
let amount = amount.as_u128();
let request_nonce = request_nonce.as_u128();
Expand All @@ -119,27 +121,53 @@ impl EthereumEventHandler {
return Ok(());
}

contract
.receive_request(
azero_connection,
while contract
.needs_signature(
azero_connection.as_connection(),
request_hash,
azero_connection.account_id().clone(),
committee_id,
dest_token_address,
amount,
dest_receiver_address,
request_nonce,
BlockStatus::Finalized,
)
.await
// default AlephClient error is MBs large and useless, dumps the entire runtime for some reason
.map_err(|_| EthereumEventHandlerError::ReceiveRequestTxFailure {
request_hash: hex::encode(request_hash),
committee_id,
dest_token_address: hex::encode(dest_token_address),
amount,
dest_receiver_address: hex::encode(dest_receiver_address),
request_nonce,
})?;

.await?
{
debug!("Azero: request with nonce {request_nonce} not yet finalized.");

if !contract
.needs_signature(
azero_connection.as_connection(),
request_hash,
azero_connection.account_id().clone(),
committee_id,
BlockStatus::Best,
)
.await?
{
sleep(AZERO_WAIT_FOR_FINALITY_CHECK).await;
continue;
}
// send vote
contract
.receive_request(
azero_connection,
request_hash,
committee_id,
dest_token_address,
amount,
dest_receiver_address,
request_nonce,
)
.await
// default AlephClient error is MBs large and useless, dumps the entire runtime for some reason
.map_err(|_| EthereumEventHandlerError::ReceiveRequestTxFailure {
request_hash: hex::encode(request_hash),
committee_id,
dest_token_address: hex::encode(dest_token_address),
amount,
dest_receiver_address: hex::encode(dest_receiver_address),
request_nonce,
})?;
}
info!("Guardian signature for 0x{request_hash_hex} no longer needed");
}

Expand Down

0 comments on commit 04cf4cc

Please sign in to comment.