Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 127 additions & 18 deletions apps/src/lib/client/eth_bridge/validator_set.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::sync::Arc;

Expand Down Expand Up @@ -25,6 +26,92 @@ use crate::client::eth_bridge::BlockOnEthSync;
use crate::control_flow::install_shutdown_signal;
use crate::facade::tendermint_rpc::{Client, HttpClient};

/// Relayer related errors.
#[derive(Debug, Default)]
enum Error {
/// An error, with no further context.
///
/// This is usually because context was already
/// provided in the form of `tracing!()` calls.
#[default]
NoContext,
/// An error message with a reason and an associated
/// `tracing` log level.
WithReason {
/// The reason of the error.
reason: Cow<'static, str>,
/// The log level where to display the error message.
level: tracing::Level,
/// If critical, exit the relayer.
critical: bool,
},
}

impl Error {
/// Create a new error message.
///
/// The error is recoverable.
fn recoverable<M>(msg: M) -> Self
where
M: Into<Cow<'static, str>>,
{
Error::WithReason {
level: tracing::Level::DEBUG,
reason: msg.into(),
critical: false,
}
}

/// Create a new error message.
///
/// The error is not recoverable.
fn critical<M>(msg: M) -> Self
where
M: Into<Cow<'static, str>>,
{
Error::WithReason {
level: tracing::Level::ERROR,
reason: msg.into(),
critical: true,
}
}

/// Exit from the relayer process, if the error
/// was critical.
fn maybe_exit(&self) {
if let Error::WithReason { critical: true, .. } = self {
safe_exit(1);
}
}

/// Display the error message.
fn display(&self) {
match self {
Error::WithReason {
reason,
level: tracing::Level::ERROR,
..
} => {
tracing::error!(
%reason,
"An error occurred during the relay"
);
}
Error::WithReason {
reason,
level: tracing::Level::DEBUG,
..
} => {
tracing::debug!(
%reason,
"An error occurred during the relay"
);
}
_ => {}
}
}
}

/// Get the status of a relay result.
trait GetStatus {
/// Return whether a relay result is successful or not.
Expand Down Expand Up @@ -338,9 +425,8 @@ pub async fn relay_validator_set_update(args: args::ValidatorSetUpdateRelay) {
)
.await;
if let Err(err) = result {
let err = err.as_ref().map(|s| s.as_ref()).unwrap_or("Unspecified");
tracing::error!(reason = err, "The relay failed");
safe_exit(1);
err.display();
err.maybe_exit();
}
}
}
Expand Down Expand Up @@ -394,7 +480,12 @@ async fn relay_validator_set_update_daemon(
// so it is best to always fetch the latest governance
// contract address
let governance =
get_governance_contract(&nam_client, Arc::clone(&eth_client)).await;
get_governance_contract(&nam_client, Arc::clone(&eth_client))
.await
.unwrap_or_else(|err| {
err.display();
safe_exit(1);
});
let governance_epoch_prep_call = governance.validator_set_nonce();
let governance_epoch_fut =
governance_epoch_prep_call.call().map(|result| {
Expand Down Expand Up @@ -469,8 +560,7 @@ async fn relay_validator_set_update_daemon(
).await;

if let Err(err) = result {
let err = err.as_ref().map(|s| s.as_ref()).unwrap_or("Unspecified");
tracing::error!(err, "An error occurred during the relay");
err.display();
last_call_succeeded = false;
}
}
Expand All @@ -479,29 +569,39 @@ async fn relay_validator_set_update_daemon(
async fn get_governance_contract(
nam_client: &HttpClient,
eth_client: Arc<Provider<Http>>,
) -> Governance<Provider<Http>> {
) -> Result<Governance<Provider<Http>>, Error> {
let governance_contract = RPC
.shell()
.eth_bridge()
.read_governance_contract(nam_client)
.await
.unwrap();
Governance::new(governance_contract.address, eth_client)
.map_err(|err| {
use namada::ledger::queries::tm::Error;
match err {
Error::Tendermint(e) => self::Error::critical(e.to_string()),
e => self::Error::recoverable(e.to_string()),
}
})?;
Ok(Governance::new(governance_contract.address, eth_client))
}

async fn relay_validator_set_update_once<R, F>(
args: &args::ValidatorSetUpdateRelay,
nam_client: &HttpClient,
mut action: F,
) -> Result<(), Option<String>>
) -> Result<(), Error>
where
R: ShouldRelay,
F: FnMut(R::RelayResult),
{
let epoch_to_relay = if let Some(epoch) = args.epoch {
epoch
} else {
RPC.shell().epoch(nam_client).await.unwrap().next()
RPC.shell()
.epoch(nam_client)
.await
.map_err(|e| Error::critical(e.to_string()))?
.next()
};
let shell = RPC.shell().eth_bridge();
let encoded_proof_fut =
Expand All @@ -521,7 +621,7 @@ where
encoded_validator_set_args_fut,
governance_address_fut
)
.map_err(|err| Some(err.to_string()))?;
.map_err(|err| Error::recoverable(err.to_string()))?;

let (bridge_hash, gov_hash, signatures): (
[u8; 32],
Expand All @@ -531,13 +631,19 @@ where
let consensus_set: ValidatorSetArgs =
abi_decode_struct(encoded_validator_set_args);

let eth_client =
Arc::new(Provider::<Http>::try_from(&args.eth_rpc_endpoint).unwrap());
let eth_client = Arc::new(
Provider::<Http>::try_from(&args.eth_rpc_endpoint).map_err(|err| {
Error::critical(format!(
"Invalid rpc endpoint: {:?}: {err}",
args.eth_rpc_endpoint
))
})?,
);
let governance = Governance::new(governance_contract.address, eth_client);

if let Err(result) = R::should_relay(epoch_to_relay, &governance) {
action(result);
return Err(None);
return Err(Error::NoContext);
}

let mut relay_op = governance.update_validators_set(
Expand All @@ -557,17 +663,20 @@ where
relay_op.tx.set_from(eth_addr.into());
}

let pending_tx = relay_op.send().await.unwrap();
let pending_tx = relay_op
.send()
.await
.map_err(|e| Error::critical(e.to_string()))?;
let transf_result = pending_tx
.confirmations(args.confirmations as usize)
.await
.map_err(|err| Some(err.to_string()))?;
.map_err(|err| Error::critical(err.to_string()))?;

let transf_result: R::RelayResult = transf_result.into();
let status = if transf_result.is_successful() {
Ok(())
} else {
Err(None)
Err(Error::NoContext)
};

action(transf_result);
Expand Down