Skip to content

Commit

Permalink
fix(validator): announce directly if no gas estimate (#2454)
Browse files Browse the repository at this point in the history
### Description

It has been reported that `contract_call.tx.max_cost()` can fail / not
exist on some networks. When that's the case, default to assuming no
additional validator gas is required and just send the tx. If the tx
fails, report the failure and suggest that gas may be insufficient in
the logged error.

### Drive-by changes

None

### Related issues

- Fixes #2426

### Backward compatibility

_Are these changes backward compatible?_

Yes

_Are there any infrastructure implications, e.g. changes that would
prohibit deploying older commits using this infra tooling?_

None


### Testing

_What kind of testing have these changes undergone?_

None

---------

Co-authored-by: Nam Chu Hoai <[email protected]>
  • Loading branch information
daniel-savu and nambrot authored Jul 4, 2023
1 parent 7889385 commit 6ee5868
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 36 deletions.
40 changes: 29 additions & 11 deletions rust/agents/validator/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use hyperlane_base::{
MessageContractSync,
};
use hyperlane_core::{
accumulator::incremental::IncrementalMerkle, Announcement, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneSigner, HyperlaneSignerExt, Mailbox, ValidatorAnnounce, H256, U256,
accumulator::incremental::IncrementalMerkle, Announcement, ChainResult, HyperlaneChain,
HyperlaneContract, HyperlaneDomain, HyperlaneSigner, HyperlaneSignerExt, Mailbox, TxOutcome,
ValidatorAnnounce, H256, U256,
};
use hyperlane_ethereum::{SingletonSigner, SingletonSignerHandle};

Expand Down Expand Up @@ -204,6 +205,27 @@ impl Validator {
tasks
}

fn log_on_announce_failure(result: ChainResult<TxOutcome>) {
match result {
Ok(outcome) => {
if !outcome.executed {
error!(
hash=?outcome.txid,
gas_used=?outcome.gas_used,
gas_price=?outcome.gas_price,
"Transaction attempting to announce validator reverted. Make sure you have enough ETH in your account to pay for gas."
);
}
}
Err(err) => {
error!(
?err,
"Failed to announce validator. Make sure you have enough ETH in your account to pay for gas."
);
}
}
}

async fn announce(&self) -> Result<()> {
// Sign and post the validator announcement
let announcement = Announcement {
Expand Down Expand Up @@ -238,7 +260,8 @@ impl Validator {
let balance_delta = self
.validator_announce
.announce_tokens_needed(signed_announcement.clone())
.await?;
.await
.unwrap_or_default();
if balance_delta > U256::zero() {
warn!(
tokens_needed=%balance_delta,
Expand All @@ -247,16 +270,11 @@ impl Validator {
);
sleep(self.interval).await;
} else {
let outcome = self
let result = self
.validator_announce
.announce(signed_announcement.clone(), None)
.await?;
if !outcome.executed {
error!(
hash=?outcome.txid,
"Transaction attempting to announce validator reverted"
);
}
.await;
Self::log_on_announce_failure(result);
}
}
}
Expand Down
39 changes: 24 additions & 15 deletions rust/chains/hyperlane-ethereum/src/validator_announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use std::collections::HashMap;
use std::sync::Arc;

use async_trait::async_trait;
use ethers::providers::{Middleware, ProviderError};
use ethers::providers::Middleware;

use ethers_contract::builders::ContractCall;
use hyperlane_core::{
Announcement, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneProvider, SignedType, TxOutcome, ValidatorAnnounce, H160, H256, U256,
};
use tracing::instrument;
use tracing::log::trace;

use crate::contracts::i_validator_announce::{
IValidatorAnnounce as EthereumValidatorAnnounceInternal, IVALIDATORANNOUNCE_ABI,
Expand Down Expand Up @@ -132,21 +133,29 @@ where
Ok(storage_locations)
}

async fn announce_tokens_needed(
&self,
announcement: SignedType<Announcement>,
) -> ChainResult<U256> {
#[instrument(ret, skip(self))]
async fn announce_tokens_needed(&self, announcement: SignedType<Announcement>) -> Option<U256> {
let validator = announcement.value.validator;
let contract_call = self.announce_contract_call(announcement, None).await?;
if let Ok(balance) = self.provider.get_balance(validator, None).await {
if let Some(cost) = contract_call.tx.max_cost() {
Ok(cost.saturating_sub(balance))
} else {
Err(ProviderError::CustomError("Unable to get announce max cost".into()).into())
}
} else {
Err(ProviderError::CustomError("Unable to query balance".into()).into())
}
let Ok(contract_call) = self
.announce_contract_call(announcement, None)
.await
else {
trace!("Unable to get announce contract call");
return None;
};

let Ok(balance) = self.provider.get_balance(validator, None).await
else {
trace!("Unable to query balance");
return None;
};

let Some(max_cost) = contract_call.tx.max_cost()
else {
trace!("Unable to get announce max cost");
return None;
};
Some(max_cost.saturating_sub(balance))
}

#[instrument(err, ret, skip(self))]
Expand Down
7 changes: 2 additions & 5 deletions rust/hyperlane-core/src/traits/validator_announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ pub trait ValidatorAnnounce: HyperlaneContract + Send + Sync + Debug {
) -> ChainResult<TxOutcome>;

/// Returns the number of additional tokens needed to pay for the announce
/// transaction.
async fn announce_tokens_needed(
&self,
announcement: SignedType<Announcement>,
) -> ChainResult<U256>;
/// transaction. Return `None` if the needed tokens canno tbe determined.
async fn announce_tokens_needed(&self, announcement: SignedType<Announcement>) -> Option<U256>;
}
7 changes: 2 additions & 5 deletions rust/hyperlane-test/src/mocks/validator_announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ mock! {
fn _announce_tokens_needed(
&self,
announcement: SignedType<Announcement>,
) -> ChainResult<U256>;
) -> Option<U256>;
}
}

Expand Down Expand Up @@ -64,10 +64,7 @@ impl ValidatorAnnounce for MockValidatorAnnounceContract {
) -> ChainResult<TxOutcome> {
self._announce(announcement, tx_gas_limit)
}
async fn announce_tokens_needed(
&self,
announcement: SignedType<Announcement>,
) -> ChainResult<U256> {
async fn announce_tokens_needed(&self, announcement: SignedType<Announcement>) -> Option<U256> {
self._announce_tokens_needed(announcement)
}
}

0 comments on commit 6ee5868

Please sign in to comment.