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

Add untested/unintegrated nomad-substrate crate #231

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6c6fbbe
feat(nomad-substrate): add nomad-substrate stubs
luketchang Aug 11, 2022
205f197
temp(nomad-substrate): save before updating patch for primitive types…
luketchang Aug 12, 2022
7d7ead2
temp(nomad-substrate): save up to implementation of Home trait for Su…
luketchang Aug 16, 2022
d476062
fix(nomad-substrate): workarounds for type issues with From impls
luketchang Aug 16, 2022
3786a53
feat(nomad-substrate): SubstrateHome supports update event indexing
luketchang Aug 17, 2022
3c7d81b
temp(nomad-substrate): codegen_home macro works with CommonIndexer
luketchang Aug 17, 2022
10ce39e
temp(nomad-substrate): saving stubs for dynamic tx/storage subxt
luketchang Aug 18, 2022
aa53736
feat(accumulator): make light merkle serializable
luketchang Aug 18, 2022
d4c3bf3
prog(nomad-substrate): home implements Common, nomad-core translates …
luketchang Aug 18, 2022
5dad4f7
feat(nomad-substrate): adds report_tx macro
luketchang Aug 18, 2022
4816320
fix(nomad-substrate): separate home and indexer
luketchang Aug 18, 2022
eaf8b43
feat(nomad-substrate): add HomeIndexer impl to SubstrateHomeIndexer
luketchang Aug 19, 2022
43b5697
temp(nomad-substrate): add stubs for Home impl for SubstrateHome, sav…
luketchang Aug 19, 2022
7671e0f
feat(nomad-substrate): finish initial Home impl for SubstrateHome
luketchang Aug 19, 2022
a4f9c3b
temp(nomad-ethereum): converts EthereumHome errors to Self::Error ins…
luketchang Aug 21, 2022
ae01ec3
refactor(nomad-core/ethereum/substrate): replace core ChainCommunicat…
luketchang Aug 21, 2022
987d089
refactor(nomad-substrate): add NomadOnlineClient and have CommonIndex…
luketchang Aug 22, 2022
47b8e97
refactor(nomad-substrate): HomeIndexer uses NomadOnlineClient method …
luketchang Aug 22, 2022
8acc982
refactor(nomad-substrate): make fetching of merkle object implemented…
luketchang Aug 22, 2022
f5c5f0a
fix(nomad-substrate): implement tx in block to successful tx events c…
luketchang Aug 22, 2022
79782c2
feat(nomad-substrate): add unimplemented replica and connection manag…
luketchang Aug 22, 2022
0f3650d
fix(clippy): fix lint errors
luketchang Aug 22, 2022
a08fb59
fix(changelogs): update relevant changelogs
luketchang Aug 22, 2022
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
1,778 changes: 1,695 additions & 83 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"nomad-base",
"nomad-test",
"chains/nomad-ethereum",
"chains/nomad-substrate",
"agents/kathy",
"agents/updater",
"agents/relayer",
Expand Down
1 change: 1 addition & 0 deletions accumulator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

### Unreleased

- Adds deserialization implementation for generic arrays to allow `LightMerkle<N>` to derive `Serialize/Deserialize`
- adds a changelog
70 changes: 69 additions & 1 deletion accumulator/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@ use ethers::{core::types::H256, prelude::U256};
use crate::{
error::IngestionError, utils::hash_concat, Merkle, MerkleProof, Proof, TREE_DEPTH, ZERO_HASHES,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy)]
/// An incremental merkle tree, modeled on the eth2 deposit contract
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct LightMerkle<const N: usize> {
#[serde(with = "arrays")]
branch: [H256; N],
count: usize,
}

impl<const N: usize> LightMerkle<N> {
/// Instantiate new LightMerkle from branch and count
pub fn new(branch: [H256; N], count: usize) -> Self {
Self { branch, count }
}
}

impl<const N: usize> Default for LightMerkle<N> {
fn default() -> Self {
let mut branch: [H256; N] = [Default::default(); N];
Expand Down Expand Up @@ -106,6 +115,65 @@ impl<const N: usize> LightMerkle<N> {
}
}

mod arrays {
use std::{convert::TryInto, marker::PhantomData};

use serde::{
de::{SeqAccess, Visitor},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
pub fn serialize<S: Serializer, T: Serialize, const N: usize>(
data: &[T; N],
ser: S,
) -> Result<S::Ok, S::Error> {
let mut s = ser.serialize_tuple(N)?;
for item in data {
s.serialize_element(item)?;
}
s.end()
}

struct ArrayVisitor<T, const N: usize>(PhantomData<T>);

impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor<T, N>
where
T: Deserialize<'de>,
{
type Value = [T; N];

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(&format!("an array of length {}", N))
}

#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
// can be optimized using MaybeUninit
let mut data = Vec::with_capacity(N);
for _ in 0..N {
match (seq.next_element())? {
Some(val) => data.push(val),
None => return Err(serde::de::Error::invalid_length(N, &self)),
}
}
match data.try_into() {
Ok(arr) => Ok(arr),
Err(_) => unreachable!(),
}
}
}
pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
deserializer.deserialize_tuple(N, ArrayVisitor::<T, N>(PhantomData))
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
8 changes: 4 additions & 4 deletions agents/processor/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ use tracing::{
};

use nomad_base::{
cancel_task, decl_agent, decl_channel, AgentCore, CachingHome, CachingReplica, NomadAgent,
NomadDB, ProcessorError,
cancel_task, decl_agent, decl_channel, AgentCore, CachingHome, CachingReplica,
ChainCommunicationError, NomadAgent, NomadDB, ProcessorError,
};
use nomad_core::{
accumulator::{MerkleProof, NomadProof},
ChainCommunicationError, CommittedMessage, Common, Home, HomeEvents, MessageStatus,
CommittedMessage, Common, Home, HomeEvents, MessageStatus,
};

use crate::{prover_sync::ProverSync, push::Pusher, settings::ProcessorSettings as Settings};
Expand Down Expand Up @@ -259,7 +259,7 @@ impl Replica {
// Other errors are bubbled up
match result {
Ok(_) => {}
Err(ChainCommunicationError::NotExecuted(txid)) => {
Err(ChainCommunicationError::TxNotExecuted(txid)) => {
warn!(txid = ?txid, "Error in processing. May indicate an internal revert of the handler.");
}
Err(e) => {
Expand Down
3 changes: 1 addition & 2 deletions agents/processor/src/prover_sync.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use color_eyre::eyre::{bail, Result};
use ethers::core::types::H256;
use nomad_base::NomadDB;
use nomad_base::{ChainCommunicationError, NomadDB};
use nomad_core::{
accumulator::{Merkle, NomadTree, ProvingError},
db::DbError,
ChainCommunicationError,
};
use std::{fmt::Display, time::Duration};
use tokio::{
Expand Down
11 changes: 2 additions & 9 deletions agents/relayer/src/relayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,11 @@ impl NomadAgent for Relayer {

#[cfg(test)]
mod test {
use ethers::prelude::{ProviderError, H256};
use ethers::prelude::H256;
use nomad_base::{
chains::PageSettings, CommonIndexers, ContractSync, ContractSyncMetrics, CoreMetrics,
HomeIndexers, IndexSettings, NomadDB,
};
use nomad_core::ChainCommunicationError;
use nomad_test::mocks::{MockHomeContract, MockIndexer, MockReplicaContract};
use nomad_test::test_utils;
use std::collections::HashMap;
Expand Down Expand Up @@ -261,13 +260,7 @@ mod test {
replica_mock
.expect__committed_root()
.times(..)
.returning(|| {
Err(ChainCommunicationError::ProviderError(
ProviderError::CustomError(
"I am replica and I always throw the error".to_string(),
),
))
});
.returning(|| Err(nomad_test::mocks::MockError));
}

let replica_indexer: Arc<CommonIndexers> = Arc::new(MockIndexer::new().into());
Expand Down
7 changes: 4 additions & 3 deletions agents/watcher/src/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use tokio::{
use tracing::{error, info, info_span, instrument::Instrumented, Instrument};

use nomad_base::{
cancel_task, AgentCore, BaseError, CachingHome, ConnectionManagers, NomadAgent, NomadDB,
cancel_task, AgentCore, BaseError, CachingHome, ChainCommunicationError, ConnectionManagers,
NomadAgent, NomadDB,
};
use nomad_core::{
ChainCommunicationError, Common, CommonEvents, ConnectionManager, DoubleUpdate,
FailureNotification, Home, SignedFailureNotification, SignedUpdate, Signers, TxOutcome,
Common, CommonEvents, ConnectionManager, DoubleUpdate, FailureNotification, Home,
SignedFailureNotification, SignedUpdate, Signers, TxOutcome,
};

use crate::settings::WatcherSettings as Settings;
Expand Down
2 changes: 2 additions & 0 deletions chains/nomad-ethereum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Unreleased

- Add `EthereumError` error enum to wrap ethers and gelato errors (ethereum-specific)
- Make existing contract methods return `Result<_, EthereumError>` now instead of using old `nomad_core::ChainCommunicationError`
- impl `std::fmt::Display` for `EthereumHome` and `EthereumReplica`
- use gelato-sdk as a github dep rather than a crate
- adds a changelog
39 changes: 39 additions & 0 deletions chains/nomad-ethereum/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::gelato::GelatoError;
use ethers::core::types::H256;
use ethers::prelude::{ContractError, Middleware, ProviderError};
use std::error::Error as StdError;

/// Ethereum-specific error wrapper
#[derive(Debug, thiserror::Error)]
pub enum EthereumError {
/// Ethers provider error
#[error("{0}")]
ProviderError(#[from] ProviderError),
/// Ethers contract error
#[error("{0}")]
ContractError(Box<dyn StdError + Send + Sync>),
/// Middleware error
#[error("{0}")]
MiddlewareError(Box<dyn StdError + Send + Sync>),
/// Gelato client error
#[error("{0}")]
GelatoError(#[from] GelatoError),
/// A transaction was dropped from the mempool
#[error("Transaction dropped from mempool {0:?}")]
DroppedError(H256),
/// Transaction was not executed successfully
#[error("Transaction was not executed successfully {0:?}")]
TxNotExecuted(H256),
/// Any other error
#[error("{0}")]
CustomError(#[from] Box<dyn StdError + Send + Sync>),
}

impl<M> From<ContractError<M>> for EthereumError
where
M: Middleware + 'static,
{
fn from(e: ContractError<M>) -> Self {
Self::ContractError(e.into())
}
}
61 changes: 35 additions & 26 deletions chains/nomad-ethereum/src/gelato.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use ethers::{
};
use gelato_sdk::{
get_forwarder,
rpc::{RelayResponse, TaskState},
rpc::{CheckOrDate, RelayResponse, TaskState},
FeeToken, ForwardRequestBuilder, GelatoClient,
};
use std::sync::Arc;
use std::{error::Error as StdError, sync::Arc};
use tokio::task::JoinHandle;
use tokio::time::{sleep, Duration};
use tracing::info;

use nomad_core::{ChainCommunicationError, Signers, TxOutcome};
use nomad_core::{Signers, TxOutcome};

pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [
TaskState::CheckPending,
Expand All @@ -22,6 +22,25 @@ pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [
TaskState::WaitingForConfirmation,
];

/// Gelato-specific errors
#[derive(Debug, thiserror::Error)]
pub enum GelatoError {
/// Gelato client error
#[error("{0}")]
ClientError(#[from] gelato_sdk::ClientError),
/// Failed task error
#[error("Gelato task failed. Id: {task_id}. Check info: {check_info:?}.")]
FailedTaskError {
/// Task id
task_id: H256,
/// Status
check_info: Option<CheckOrDate>,
},
/// Custom error
#[error("{0}")]
CustomError(#[from] Box<dyn StdError + Send + Sync>),
}

/// Gelato client for submitting txs to single chain
#[derive(Debug, Clone)]
pub struct SingleChainGelatoClient<M> {
Expand Down Expand Up @@ -75,7 +94,7 @@ where
domain: u32,
contract_address: Address,
tx: &TypedTransaction,
) -> Result<TxOutcome, ChainCommunicationError> {
) -> Result<TxOutcome, GelatoError> {
let task_id = self
.dispatch_tx(domain, contract_address, tx)
.await?
Expand All @@ -86,7 +105,7 @@ where
info!(task_id = ?&task_id, "Polling Gelato task...");
self.poll_task_id(task_id)
.await
.map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?
.map_err(|e| GelatoError::CustomError(e.into()))?
}

/// Dispatch tx to Gelato and return task id.
Expand All @@ -95,7 +114,7 @@ where
domain: u32,
contract_address: Address,
tx: &TypedTransaction,
) -> Result<RelayResponse, ChainCommunicationError> {
) -> Result<RelayResponse, GelatoError> {
// If gas limit not hardcoded in tx, eth_estimateGas
let gas_limit = tx
.gas()
Expand All @@ -104,7 +123,7 @@ where
.eth_client
.estimate_gas(tx)
.await
.map_err(|e| ChainCommunicationError::MiddlewareError(e.into()))?,
.map_err(|e| GelatoError::CustomError(e.into()))?,
)
.as_u64()
.into();
Expand All @@ -118,28 +137,22 @@ where

self.send_forward_request(contract_address, data, gas_limit)
.await
.map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))
}

/// Poll task id and return tx hash of transaction if successful, error if
/// otherwise.
pub fn poll_task_id(
&self,
task_id: H256,
) -> JoinHandle<Result<TxOutcome, ChainCommunicationError>> {
pub fn poll_task_id(&self, task_id: H256) -> JoinHandle<Result<TxOutcome, GelatoError>> {
let gelato = self.gelato();

tokio::spawn(async move {
loop {
let status = gelato
.get_task_status(task_id)
.await
.map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?;
let status = gelato.get_task_status(task_id).await?;

if !ACCEPTABLE_STATES.contains(&status.task_state) {
return Err(ChainCommunicationError::TxSubmissionError(
format!("Gelato task failed: {:?}", status).into(),
));
return Err(GelatoError::FailedTaskError {
task_id,
check_info: status.last_check,
});
}

if let Some(execution) = &status.execution {
Expand Down Expand Up @@ -172,15 +185,14 @@ where
target: Address,
data: impl Into<Bytes>,
gas_limit: U64,
) -> Result<RelayResponse, ChainCommunicationError> {
) -> Result<RelayResponse, GelatoError> {
// add 100k gas padding for Gelato contract ops
let adjusted_limit = gas_limit + U64::from(100_000);

let max_fee = self
.gelato()
.get_estimated_fee(self.chain_id, self.fee_token, adjusted_limit, false)
.await
.map_err(|e| ChainCommunicationError::CustomError(e.into()))?;
.await?;

let request = ForwardRequestBuilder::default()
.chain_id(self.chain_id)
Expand All @@ -201,9 +213,6 @@ where
"Signed gelato forward request."
);

self.gelato()
.send_forward_request(&request)
.await
.map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))
Ok(self.gelato().send_forward_request(&request).await?)
}
}
Loading