Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A0-4251: Avoid resumbissions on Aleph via better handling loop #177

Merged
merged 12 commits into from
Apr 29, 2024
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,
)
ggawryal marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading