Skip to content

Commit

Permalink
Bootstrap relayer gadget (#671)
Browse files Browse the repository at this point in the history
* bootstrap relayer gadget

* integrated the relayer gadget into the node

* add example config and it works

* fix the gmp linking

* fix linking on macos with nix

* improve the code

* Update relayer to v0.5.6-dev
  • Loading branch information
shekohex authored Jul 12, 2023
1 parent 90bfe77 commit 23eeca7
Show file tree
Hide file tree
Showing 13 changed files with 4,008 additions and 701 deletions.
4,433 changes: 3,744 additions & 689 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ members = [
'dkg-gadget',
'dkg-logging',
'dkg-mock-blockchain',
'dkg-test-orchestrator'
'dkg-test-orchestrator',
'relayer-gadget',
'relayer-gadget/cli',
]
resolver = "2"

Expand All @@ -34,9 +36,12 @@ dkg-logging = { path = "dkg-logging" }
dkg-rococo-runtime = { default-features = false, path = "parachain/runtime/rococo" }
dkg-mock-blockchain = { path = "dkg-mock-blockchain", default-features = false }
dkg-test-orchestrator = { path = "dkg-test-orchestrator", default-features = false }
webb-relayer-gadget = { path = "relayer-gadget", default-features = false }
webb-relayer-gadget-cli = { path = "relayer-gadget/cli", default-features = false }

futures = "0.3.15"
ethabi = { version = "18.0.0", default-features = false }
ethereum-types = { version = "0.14.1", default-features = false }
clap = { version = "4.0.32", features = ["derive"] }
rand = "0.8.4"
hex-literal = { package = "hex-literal", version = "0.3.3" }
Expand Down
11 changes: 6 additions & 5 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@
devShells.default = pkgs.mkShell {
name = "dkg";
nativeBuildInputs = [
pkgs.gmp
pkgs.protobuf
pkgs.pkg-config
# Needed for rocksdb-sys
pkgs.clang
pkgs.libclang.lib
pkgs.rustPlatform.bindgenHook
pkgs.gmp
# Mold Linker for faster builds (only on Linux)
(lib.optionals pkgs.stdenv.isLinux pkgs.mold)
(lib.optionals pkgs.stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.Security)
(lib.optionals pkgs.stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.SystemConfiguration)
];
buildInputs = [
# We want the unwrapped version, wrapped comes with nixpkgs' toolchain
Expand All @@ -48,11 +50,10 @@
toolchain
];
packages = [ ];

# Environment variables
# Environment variables
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
# Needed for running DKG Node.
LD_LIBRARY_PATH = "${pkgs.gmp}/lib";
# Needed for running DKG Node.
LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.gmp ];
};
});
}
27 changes: 27 additions & 0 deletions relayer-gadget/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "webb-relayer-gadget"
version = "0.0.1"
authors = ["Webb Developers <[email protected]>", "Shady Khalifa <[email protected]>"]
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
homepage = { workspace = true }
repository = { workspace = true }
edition = { workspace = true }

[dependencies]
tracing = { workspace = true }
ethereum-types = { workspace = true }

# Substrate
sp-application-crypto = { workspace = true }
sp-keystore = { workspace = true }
sc-keystore = { workspace = true }

# Relayer
webb-relayer = { git = "https://github.com/webb-tools/relayer", tag = "v0.5.6-dev" }
webb-relayer-context = { git = "https://github.com/webb-tools/relayer", tag = "v0.5.6-dev" }
webb-relayer-config = { git = "https://github.com/webb-tools/relayer", tag = "v0.5.6-dev" }
webb-relayer-store = { git = "https://github.com/webb-tools/relayer", tag = "v0.5.6-dev" }
webb-relayer-types = { git = "https://github.com/webb-tools/relayer", tag = "v0.5.6-dev" }

# DKG
dkg-runtime-primitives = { workspace = true }
11 changes: 11 additions & 0 deletions relayer-gadget/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "webb-relayer-gadget-cli"
version = "0.0.1"
authors = ["Webb Developers <[email protected]>", "Shady Khalifa <[email protected]>"]
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
homepage = { workspace = true }
repository = { workspace = true }
edition = { workspace = true }

[dependencies]
clap = { workspace = true }
10 changes: 10 additions & 0 deletions relayer-gadget/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use std::path::PathBuf;

/// Cli tool to interact with Webb Relayer CLI
#[derive(Debug, Clone, clap::Parser)]
#[clap(next_help_heading = "Webb Relayer")]
pub struct WebbRelayerCmd {
/// Directory that contains configration files for the relayer.
#[arg(long, value_name = "PATH")]
pub relayer_config_dir: Option<PathBuf>,
}
3 changes: 3 additions & 0 deletions relayer-gadget/config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Configuration Examples

This directory contains a very simple and minimal configuration for starting a Private Transaction Relayer over the Gorli Testnet. This configuration is used for testing and for demonstration purposes.
42 changes: 42 additions & 0 deletions relayer-gadget/config/simple.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
port = 9955

# Controls what features are enabled in the relayer system
[features]
# if you are an authority, this always true.
governance-relay = true
data-query = true
private-tx-relay = true

[evm.goerli]
name = "goerli"
http-endpoint = "https://rpc.ankr.com/eth_goerli"
ws-endpoint = "wss://rpc.ankr.com/eth_goerli"
chain-id = 5
enabled = true
block-confirmations = 2
# The private key of the account that will be used to sign transactions
# If not set, we will use the Keystore to get the ECDSA private key.
# private-key = "$PRIVATE_KEY"

[[evm.goerli.contracts]]
contract = "VAnchor"
address = "0x38e7aa90c77f86747fab355eecaa0c2e4c3a463d"
deployed-at = 8703495
events-watcher = { enabled = true, polling-interval = 15000 }
proposal-signing-backend = { type = "DKGNode", chain-id = 0 }

[substrate.internal]
name = "internal"
chain-id = 0
http-endpoint = "http://localhost:9933"
ws-endpoint = "ws://localhost:9944"
suri = "//Alice"
enabled = true

[[substrate.internal.pallets]]
pallet = "DKG"
events-watcher = { enabled = true, polling-interval = 3000, print-progress-interval = 30000 }

[[substrate.internal.pallets]]
pallet = "DKGProposalHandler"
events-watcher = { enabled = true, polling-interval = 3000, print-progress-interval = 30000 }
119 changes: 119 additions & 0 deletions relayer-gadget/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! Webb Relayer Gadget
//!
//! Integrates the Webb Relayer into the Substrate Node.
use dkg_runtime_primitives::crypto;
use ethereum_types::Secret;
use sc_keystore::LocalKeystore;
use sp_application_crypto::{ecdsa, ByteArray, CryptoTypePublicPair, Pair};
use sp_keystore::SyncCryptoStore;
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
use webb_relayer::service;
use webb_relayer_context::RelayerContext;

/// Webb Relayer gadget initialization parameters.
pub struct WebbRelayerParams {
/// Concrete local key store
pub local_keystore: Option<Arc<LocalKeystore>>,
/// Configuration directory
pub config_dir: Option<PathBuf>,
/// Database path
pub database_path: Option<PathBuf>,
/// RPC HTTP address, `None` if disabled.
pub rpc_http: Option<SocketAddr>,
/// RPC WebSocket address, `None` if disabled.
pub rpc_ws: Option<SocketAddr>,
}

pub async fn start_relayer_gadget(relayer_params: WebbRelayerParams) {
let mut config = match relayer_params.config_dir.as_ref() {
Some(p) => load_config(p).expect("failed to load relayer config"),
None => {
tracing::error!(
target: "relayer-gadget",
"Error: Not Starting Webb Relayer Gadget. No Config Directory Specified"
);
return
},
};

post_process_config(&mut config, &relayer_params)
.expect("failed to post process relayer config");

let store = create_store(relayer_params.database_path).expect("failed to create relayer store");
let ctx = RelayerContext::new(config, store.clone()).expect("failed to build relayer context");

// Start the web server:
service::build_web_services(ctx.clone())
.await
.expect("failed to build relayer web services");
service::ignite(ctx, Arc::new(store))
.await
.expect("failed to ignite relayer services");
}

/// Loads the configuration from the given directory.
fn load_config(
config_dir: &PathBuf,
) -> Result<webb_relayer_config::WebbRelayerConfig, Box<dyn std::error::Error>> {
if !config_dir.is_dir() {
return Err("Config path is not a directory".into())
}

Ok(webb_relayer_config::utils::load(config_dir)?)
}

/// Creates a database store for the relayer based on the configuration passed in.
pub fn create_store(
database_path: Option<PathBuf>,
) -> Result<webb_relayer_store::SledStore, Box<dyn std::error::Error>> {
let db_path = match database_path {
Some(p) => p.join("relayerdb"),
None => {
tracing::debug!("Using temp dir for store");
return webb_relayer_store::SledStore::temporary().map_err(Into::into)
},
};

webb_relayer_store::SledStore::open(db_path).map_err(Into::into)
}

/// Post process the relayer configuration.
///
/// - if there is no signer for any EVM chain, set the signer to the ecdsa key from the
/// keystore.
/// - Ensures that governance relayer is always enabled.
fn post_process_config(
config: &mut webb_relayer_config::WebbRelayerConfig,
params: &WebbRelayerParams,
) -> Result<(), Box<dyn std::error::Error>> {
// Make sure governance relayer is always enabled
config.features.governance_relay = true;
let ecdsa_pair = get_ecdsa_pair(params.local_keystore.clone())?.ok_or("no ecdsa key found")?;
let ecdsa_secret = ecdsa_pair.to_raw_vec();
// for each evm chain, if there is no signer, set the signer to the ecdsa key
for chain in config.evm.values_mut() {
if chain.private_key.is_none() {
chain.private_key = Some(Secret::from_slice(&ecdsa_secret).into())
}
}
Ok(())
}

fn get_ecdsa_pair(
local_keystore: Option<Arc<LocalKeystore>>,
) -> Result<Option<crypto::Pair>, Box<dyn std::error::Error>> {
let local_key_store = local_keystore.expect("failed to get local keystore");
let ecdsa_public = local_key_store
.keys(dkg_runtime_primitives::KEY_TYPE)?
.into_iter()
.find_map(|CryptoTypePublicPair(id, public_key)| {
if id == ecdsa::CRYPTO_ID {
crypto::Public::from_slice(&public_key).ok()
} else {
None
}
})
.ok_or("failed to get ecdsa public key")?;
local_key_store.key_pair::<crypto::Pair>(&ecdsa_public).map_err(Into::into)
}
10 changes: 7 additions & 3 deletions standalone/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ dkg-runtime-primitives = { workspace = true }
dkg-primitives = { workspace = true }
dkg-standalone-runtime = { workspace = true }
dkg-logging = { workspace = true }
webb-relayer-gadget = { workspace = true }
webb-relayer-gadget-cli = { workspace = true }
tokio = { workspace = true, features = ["rt"] }


Expand All @@ -74,9 +76,11 @@ substrate-build-script-utils = { workspace = true }

[features]
default = []
runtime-benchmarks = ["dkg-standalone-runtime/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-benchmarking-cli/runtime-benchmarks",]
runtime-benchmarks = [
"dkg-standalone-runtime/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-benchmarking-cli/runtime-benchmarks",
]
integration-tests = ["dkg-standalone-runtime/integration-tests"]
manual-seal = ["dkg-standalone-runtime/manual-seal"]
tracing = ["dkg-gadget/debug-tracing"]
Expand Down
2 changes: 2 additions & 0 deletions standalone/node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub struct Cli {
pub run: RunCmd,
#[arg(long, short = 'o')]
pub output_path: Option<std::path::PathBuf>,
#[clap(flatten)]
pub relayer_cmd: webb_relayer_gadget_cli::WebbRelayerCmd,
}

#[derive(Debug, clap::Subcommand)]
Expand Down
7 changes: 6 additions & 1 deletion standalone/node/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,12 @@ pub fn run() -> sc_cli::Result<()> {
}

runner.run_node_until_exit(|config| async move {
service::new_full(config, cli.output_path).map_err(sc_cli::Error::Service)
service::new_full(service::RunFullParams {
config,
debug_output: cli.output_path,
relayer_cmd: cli.relayer_cmd,
})
.map_err(sc_cli::Error::Service)
})
},
}
Expand Down
27 changes: 25 additions & 2 deletions standalone/node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,15 @@ fn remote_keystore(_url: &str) -> Result<Arc<LocalKeystore>, &'static str> {
Err("Remote Keystore not supported.")
}

pub struct RunFullParams {
pub config: Configuration,
pub debug_output: Option<std::path::PathBuf>,
pub relayer_cmd: webb_relayer_gadget_cli::WebbRelayerCmd,
}

/// Builds a new service for a full client.
pub fn new_full(
mut config: Configuration,
debug_output: Option<std::path::PathBuf>,
RunFullParams { mut config, debug_output, relayer_cmd }: RunFullParams,
) -> Result<TaskManager, ServiceError> {
let sc_service::PartialComponents {
client,
Expand Down Expand Up @@ -286,6 +291,24 @@ pub fn new_full(
None,
dkg_gadget::start_dkg_gadget::<_, _, _>(dkg_params),
);

let relayer_params = webb_relayer_gadget::WebbRelayerParams {
local_keystore: keystore_container.local_keystore(),
config_dir: relayer_cmd.relayer_config_dir,
database_path: config
.database
.path()
.and_then(|path| path.parent())
.map(|p| p.to_path_buf()),
rpc_http: config.rpc_http,
rpc_ws: config.rpc_ws,
};
// Start Webb Relayer Gadget as non-essential task.
task_manager.spawn_handle().spawn(
"relayer-gadget",
None,
webb_relayer_gadget::start_relayer_gadget(relayer_params),
);
}

let rpc_extensions_builder = {
Expand Down

0 comments on commit 23eeca7

Please sign in to comment.