Skip to content

Commit

Permalink
refactor: unify dvn and executor workers (#309)
Browse files Browse the repository at this point in the history
Changes addressed previous FIXMEs, including:
- Move DVN components around so it's more modular
- Fix a minor bug on the executor, where the `provider` went
out of scope and stopped listening.
- Add some tests for the DVN.
  • Loading branch information
palozano authored Nov 7, 2024
1 parent 779613f commit c4f3a9c
Show file tree
Hide file tree
Showing 29 changed files with 574 additions and 475 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,4 @@ setup/plugin/config/keys/ecdsa.json
libnear_da_rpc_sys.*

# Ignore configuration for DVN or Executor
**/config_dvn.toml
**/config_executor.toml
#offchain/workers_config.toml
54 changes: 31 additions & 23 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "2"
members = [
"indexer",
"contracts/evm/test/ffi/bls-utils",
"workers",
"offchain",
]

[workspace.package]
Expand Down
8 changes: 5 additions & 3 deletions workers/Cargo.toml → offchain/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "workers"
name = "offchain"
version = "0.1.0"
edition = "2021"

Expand All @@ -18,16 +18,18 @@ bytes = "1.7.2"
config = { version = "0.14.0", features = ["toml"] }
eyre.workspace = true
futures = "0.3.31"
log = "0.4.22"
project-root = "0.2.2"
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber = {workspace = true, features = ["env-filter"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tokio-tungstenite = "0.24.0"
log = "0.4.22"

[dev-dependencies]
axum = "0.7.7"
http-body-util = "0.1.0"
wiremock = "0.6.2"
tempfile = "3.13.0"
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions offchain/abi/ReceiveLibUln302.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"abi":[{"inputs":[{"internalType":"address","name":"_endpoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"LZ_MessageLib_OnlyEndpoint","type":"error"},{"inputs":[],"name":"LZ_ULN_AtLeastOneDVN","type":"error"},{"inputs":[{"internalType":"uint32","name":"configType","type":"uint32"}],"name":"LZ_ULN_InvalidConfigType","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidConfirmations","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidEid","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidOptionalDVNCount","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidOptionalDVNThreshold","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidPacketHeader","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidPacketVersion","type":"error"},{"inputs":[],"name":"LZ_ULN_InvalidRequiredDVNCount","type":"error"},{"inputs":[],"name":"LZ_ULN_Unsorted","type":"error"},{"inputs":[{"internalType":"uint32","name":"eid","type":"uint32"}],"name":"LZ_ULN_UnsupportedEid","type":"error"},{"inputs":[],"name":"LZ_ULN_Verifying","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint32","name":"eid","type":"uint32"},{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"internalType":"struct UlnConfig","name":"config","type":"tuple"}],"indexed":false,"internalType":"struct SetDefaultUlnConfigParam[]","name":"params","type":"tuple[]"}],"name":"DefaultUlnConfigsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"dvn","type":"address"},{"indexed":false,"internalType":"bytes","name":"header","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"confirmations","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"proofHash","type":"bytes32"}],"name":"PayloadVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oapp","type":"address"},{"indexed":false,"internalType":"uint32","name":"eid","type":"uint32"},{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"indexed":false,"internalType":"struct UlnConfig","name":"config","type":"tuple"}],"name":"UlnConfigSet","type":"event"},{"inputs":[{"internalType":"bytes","name":"_packetHeader","type":"bytes"},{"internalType":"uint32","name":"_localEid","type":"uint32"}],"name":"assertHeader","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"_packetHeader","type":"bytes"},{"internalType":"bytes32","name":"_payloadHash","type":"bytes32"}],"name":"commitVerification","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oapp","type":"address"},{"internalType":"uint32","name":"_remoteEid","type":"uint32"}],"name":"getAppUlnConfig","outputs":[{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"internalType":"struct UlnConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_eid","type":"uint32"},{"internalType":"address","name":"_oapp","type":"address"},{"internalType":"uint32","name":"_configType","type":"uint32"}],"name":"getConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_oapp","type":"address"},{"internalType":"uint32","name":"_remoteEid","type":"uint32"}],"name":"getUlnConfig","outputs":[{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"internalType":"struct UlnConfig","name":"rtnConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"headerHash","type":"bytes32"},{"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"internalType":"address","name":"dvn","type":"address"}],"name":"hashLookup","outputs":[{"internalType":"bool","name":"submitted","type":"bool"},{"internalType":"uint64","name":"confirmations","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_eid","type":"uint32"}],"name":"isSupportedEid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageLibType","outputs":[{"internalType":"enum MessageLibType","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oapp","type":"address"},{"components":[{"internalType":"uint32","name":"eid","type":"uint32"},{"internalType":"uint32","name":"configType","type":"uint32"},{"internalType":"bytes","name":"config","type":"bytes"}],"internalType":"struct SetConfigParam[]","name":"_params","type":"tuple[]"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"eid","type":"uint32"},{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"internalType":"struct UlnConfig","name":"config","type":"tuple"}],"internalType":"struct SetDefaultUlnConfigParam[]","name":"_params","type":"tuple[]"}],"name":"setDefaultUlnConfigs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"confirmations","type":"uint64"},{"internalType":"uint8","name":"requiredDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNCount","type":"uint8"},{"internalType":"uint8","name":"optionalDVNThreshold","type":"uint8"},{"internalType":"address[]","name":"requiredDVNs","type":"address[]"},{"internalType":"address[]","name":"optionalDVNs","type":"address[]"}],"internalType":"struct UlnConfig","name":"_config","type":"tuple"},{"internalType":"bytes32","name":"_headerHash","type":"bytes32"},{"internalType":"bytes32","name":"_payloadHash","type":"bytes32"}],"name":"verifiable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_packetHeader","type":"bytes"},{"internalType":"bytes32","name":"_payloadHash","type":"bytes32"},{"internalType":"uint64","name":"_confirmations","type":"uint64"}],"name":"verify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint64","name":"major","type":"uint64"},{"internalType":"uint8","name":"minor","type":"uint8"},{"internalType":"uint8","name":"endpointVersion","type":"uint8"}],"stateMutability":"pure","type":"function"}]}
File renamed without changes.
20 changes: 2 additions & 18 deletions workers/src/abi.rs → offchain/src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Types create from the JSON ABI files.
//!
//! For example, to be able to decode the logs' data, or call contracts' methods.
//! Types from the JSON ABI files. For example, to be able to decode the logs' data, or call
//! contracts' methods.
//!
//! To obtain the corresponding ABI, there are two ways:
//! - Manually downloading the ABI from the contract's source code (we use this one for now);
Expand All @@ -24,18 +23,3 @@ sol!(
L0V2EndpointAbi,
"abi/L0V2Endpoint.json"
);

//sol!(
// #[allow(missing_docs)]
// #[sol(abi)]
// #[derive(Debug, PartialEq, Eq)]
// struct Packet {
// uint64 nonce;
// uint32 src_eid;
// bytes32 sender;
// uint32 dst_eid;
// bytes32 receiver;
// bytes32 guid;
// bytes message;
// }
//);
22 changes: 22 additions & 0 deletions offchain/src/bin/dvn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Main off-chain workflow for Nuff DVN.
use eyre::Result;
use offchain::workers::dvn::Dvn;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;

#[tokio::main]
async fn main() -> Result<()> {
// Initialize tracing
tracing_subscriber::fmt()
.with_target(false)
.with_env_filter(
EnvFilter::builder()
.with_default_directive(LevelFilter::DEBUG.into())
.from_env_lossy(),
)
.init();

let mut dvn = Dvn::new_from_env()?;
dvn.listen().await
}
5 changes: 2 additions & 3 deletions workers/src/bin/executor.rs → offchain/src/bin/executor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use offchain::{config, workers::executor::NFFLExecutor};
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;
use workers::config;
use workers::executor_def::NFFLExecutor;

/// Executor is expected to work with low work rate, and we have a bonus
/// from this observation - we don't need/want to care about concurrency control,
Expand All @@ -18,7 +17,7 @@ async fn main() -> eyre::Result<()> {
)
.init();

let mut executor = NFFLExecutor::new(config::DVNConfig::load_from_env()?);
let mut executor = NFFLExecutor::new(config::WorkerConfig::load_from_env()?);
executor.listen().await?;

Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
chain::HttpProvider,
config::{DVNConfig, LayerZeroEvent},
config::{LayerZeroEvent, WorkerConfig},
};
use alloy::{
eips::BlockNumberOrTag,
Expand All @@ -11,11 +11,12 @@ use alloy::{
pubsub::{PubSubFrontend, SubscriptionStream},
rpc::types::{Filter, Log},
};
use eyre::{OptionExt, Result};
use eyre::{eyre, OptionExt, Result};
use std::path::PathBuf;

/// Create the subscriptions for the DVN workflow.
pub async fn build_subscriptions(
config: &DVNConfig,
pub async fn build_dvn_subscriptions(
config: &WorkerConfig,
) -> Result<(
RootProvider<PubSubFrontend>,
SubscriptionStream<Log>,
Expand Down Expand Up @@ -49,8 +50,9 @@ pub async fn build_subscriptions(
}

pub async fn build_executor_subscriptions(
config: &DVNConfig,
config: &WorkerConfig,
) -> Result<(
RootProvider<PubSubFrontend>,
SubscriptionStream<Log>,
SubscriptionStream<Log>,
SubscriptionStream<Log>,
Expand All @@ -75,17 +77,20 @@ pub async fn build_executor_subscriptions(
.event(LayerZeroEvent::PacketVerified.as_ref())
.from_block(BlockNumberOrTag::Latest);

Ok((
provider.subscribe_logs(&packet_sent_filter).await?.into_stream(),
provider.subscribe_logs(&executor_fee_paid).await?.into_stream(),
provider.subscribe_logs(&packet_verified_filter).await?.into_stream(),
))
let ps_stream = provider.subscribe_logs(&packet_sent_filter).await?.into_stream();
let ef_stream = provider.subscribe_logs(&executor_fee_paid).await?.into_stream();
let pv_stream = provider.subscribe_logs(&packet_verified_filter).await?.into_stream();

Ok((provider, ps_stream, ef_stream, pv_stream))
}

/// Load the MessageLib ABI.
/// Load the MessageLib ABI. The path must be relative to the project root.
pub fn get_abi_from_path(path: &str) -> Result<JsonAbi> {
let path_buf = PathBuf::from(path);
let artifact_path = project_root::get_project_root()?.join(path_buf);
// Get the SendLib ABI
let artifact = std::fs::read(path)?;
let artifact =
std::fs::read(artifact_path).map_err(|e| eyre!("Cannot load config for offchain worker. Error: {:?}", e))?;
let json: serde_json::Value = serde_json::from_slice(&artifact)?;
// SAFETY: Assume `unwrap` is safe since the key has been harcoded
let abi_value = json.get("abi").ok_or_eyre("ABI not found in artifact")?;
Expand All @@ -94,7 +99,54 @@ pub fn get_abi_from_path(path: &str) -> Result<JsonAbi> {
}

/// Construct an HTTP provider given the config.
pub fn get_http_provider(config: &DVNConfig) -> Result<HttpProvider> {
pub fn get_http_provider(config: &WorkerConfig) -> Result<HttpProvider> {
let http_provider = ProviderBuilder::new().on_http(config.http_rpc_url.to_string().parse()?);
Ok(http_provider)
}

#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;

#[test]
fn test_expect_to_find_all_abis() {
get_abi_from_path("offchain/abi/ReceiveLibUln302.json").unwrap();
get_abi_from_path("offchain/abi/SendLibUln302.json").unwrap();
get_abi_from_path("offchain/abi/L0V2Endpoint.json").unwrap();
}

#[test]
fn test_get_abi_from_path() {
// Create a file inside of `env::temp_dir()`.
let mut temp_file = NamedTempFile::new_in(".").unwrap();

// Some mocked ABI info
let data = r#"{
"abi": [
{
"type": "function",
"name": "transfer",
"inputs": [
{
"type": "address",
"name": "_to",
"internalType": "address"
},
{
"type": "uint256",
"name": "_amount",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]
}"#;
writeln!(temp_file, "{}", data).unwrap();

get_abi_from_path(temp_file.path().to_str().unwrap()).unwrap();
}
}
Loading

0 comments on commit c4f3a9c

Please sign in to comment.