Skip to content

Commit 61e9f27

Browse files
feat(signer): add Web3Signer support (#196)
* Add Remote signer config option * Update docs * Update example config * Update crates/common/src/config/signer.rs Co-authored-by: ltitanb <[email protected]> * Change remote URL type to Url --------- Co-authored-by: ltitanb <[email protected]>
1 parent d24987f commit 61e9f27

File tree

5 files changed

+80
-42
lines changed

5 files changed

+80
-42
lines changed

config.example.toml

+13-4
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ frequency_get_header_ms = 300
105105
[[mux]]
106106
# Unique ID for the mux config
107107
id = "test_mux"
108-
# Which validator pubkeys to match against this mux config. This can be empty or omitted if a loader is specified.
108+
# Which validator pubkeys to match against this mux config. This can be empty or omitted if a loader is specified.
109109
# Any keys loaded via the loader will be added to this list.
110110
validator_pubkeys = [
111111
"0x80c7f782b2467c5898c5516a8b6595d75623960b4afc4f71ee07d40985d20e117ba35e7cd352a3e75fb85a8668a3b745",
@@ -124,15 +124,24 @@ id = "example-relay"
124124
headers = { X-MyCustomHeader = "ADifferentCustomValue" }
125125

126126
# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true`
127+
# Currently two types of Signer modules are supported (only one can be used at a time):
128+
# - Remote: a remote Web3Signer instance
129+
# - Local: a local Signer module
130+
# More details on the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/#local-signer)
127131
# OPTIONAL
128-
[signer]
132+
# Remote:
133+
# [signer.remote]
134+
# URL of the Web3Signer instance
135+
# url = "https://remote.signer.url"
136+
# Local:
137+
[signer.local]
129138
# Docker image to use for the Signer module.
130139
# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest
131140
docker_image = "ghcr.io/commit-boost/signer:latest"
132141
# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported:
133142
# - File: load keys from a plain text file (unsafe, use only for testing purposes)
134143
# - ValidatorsDir: load keys from a `keys` and `secrets` file/folder (ERC-2335 style keystores). More details can be found in the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/)
135-
[signer.loader]
144+
[signer.local.loader]
136145
# File: path to the keys file
137146
key_path = "./keys.example.json"
138147
# ValidatorsDir: format of the keystore (lighthouse, prysm, teku or lodestar)
@@ -152,7 +161,7 @@ key_path = "./keys.example.json"
152161
# Configuration for how the Signer module should store proxy delegations. Currently one type of store is supported:
153162
# - File: store keys and delegations from a plain text file (unsafe, use only for testing purposes)
154163
# OPTIONAL, if missing proxies are lost on restart
155-
[signer.store]
164+
[signer.local.store]
156165
# File: path to the keys file
157166
proxy_dir = "./proxies"
158167

crates/cli/src/docker_init.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use std::{
66

77
use cb_common::{
88
config::{
9-
CommitBoostConfig, LogsSettings, ModuleKind, BUILDER_PORT_ENV, BUILDER_URLS_ENV,
10-
CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV, JWTS_ENV, LOGS_DIR_DEFAULT, LOGS_DIR_ENV,
11-
METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_ENDPOINT_ENV, PBS_MODULE_NAME,
12-
PROXY_DIR_DEFAULT, PROXY_DIR_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT,
9+
CommitBoostConfig, LogsSettings, ModuleKind, SignerConfig, BUILDER_PORT_ENV,
10+
BUILDER_URLS_ENV, CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV, JWTS_ENV, LOGS_DIR_DEFAULT,
11+
LOGS_DIR_ENV, METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_ENDPOINT_ENV,
12+
PBS_MODULE_NAME, PROXY_DIR_DEFAULT, PROXY_DIR_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT,
1313
SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_DEFAULT, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV,
1414
SIGNER_MODULE_NAME, SIGNER_PORT_ENV, SIGNER_URL_ENV,
1515
},
@@ -74,7 +74,11 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
7474

7575
// address for signer API communication
7676
let signer_port = 20000;
77-
let signer_server = format!("http://cb_signer:{signer_port}");
77+
let signer_server = if let Some(SignerConfig::Remote { url }) = &cb_config.signer {
78+
url.to_string()
79+
} else {
80+
format!("http://cb_signer:{signer_port}")
81+
};
7882

7983
let builder_events_port = 30000;
8084
let mut builder_events_modules = Vec::new();
@@ -153,7 +157,11 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
153157
networks: Networks::Simple(module_networks),
154158
volumes: module_volumes,
155159
environment: Environment::KvPair(module_envs),
156-
depends_on: DependsOnOptions::Simple(vec!["cb_signer".to_owned()]),
160+
depends_on: if let Some(SignerConfig::Remote { .. }) = &cb_config.signer {
161+
DependsOnOptions::Simple(vec![])
162+
} else {
163+
DependsOnOptions::Simple(vec!["cb_signer".to_owned()])
164+
},
157165
env_file,
158166
..Service::default()
159167
}
@@ -285,7 +293,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
285293
services.insert("cb_pbs".to_owned(), Some(pbs_service));
286294

287295
// setup signer service
288-
if let Some(signer_config) = cb_config.signer {
296+
if let Some(SignerConfig::Local { docker_image, loader, store }) = cb_config.signer {
289297
if needs_signer_module {
290298
if metrics_enabled {
291299
targets.push(PrometheusTargetConfig {
@@ -319,7 +327,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
319327
let mut volumes = vec![config_volume.clone()];
320328
volumes.extend(chain_spec_volume.clone());
321329

322-
match signer_config.loader {
330+
match loader {
323331
SignerLoader::File { key_path } => {
324332
volumes.push(Volumes::Simple(format!(
325333
"{}:{}:ro",
@@ -348,7 +356,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
348356
}
349357
};
350358

351-
if let Some(store) = signer_config.store {
359+
if let Some(store) = store {
352360
match store {
353361
ProxyStore::File { proxy_dir } => {
354362
volumes.push(Volumes::Simple(format!(
@@ -372,7 +380,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
372380

373381
let signer_service = Service {
374382
container_name: Some("cb_signer".to_owned()),
375-
image: Some(signer_config.docker_image),
383+
image: Some(docker_image),
376384
networks: Networks::Simple(signer_networks),
377385
volumes,
378386
environment: Environment::KvPair(signer_envs),
@@ -381,7 +389,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()>
381389

382390
services.insert("cb_signer".to_owned(), Some(signer_service));
383391
}
384-
} else if needs_signer_module {
392+
} else if cb_config.signer.is_none() && needs_signer_module {
385393
panic!("Signer module required but no signer config provided");
386394
}
387395

crates/common/src/commit/request.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub enum EncryptionScheme {
136136
// TODO(David): This struct shouldn't be visible to module authors
137137
#[derive(Debug, Clone, Serialize, Deserialize)]
138138
pub struct GenerateProxyRequest {
139+
#[serde(rename = "pubkey")]
139140
pub consensus_pubkey: BlsPublicKey,
140141
pub scheme: EncryptionScheme,
141142
}

crates/common/src/config/signer.rs

+26-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bimap::BiHashMap;
2-
use eyre::Result;
2+
use eyre::{bail, Result};
33
use serde::{Deserialize, Serialize};
4+
use url::Url;
45

56
use super::{
67
constants::SIGNER_IMAGE_DEFAULT,
@@ -13,14 +14,23 @@ use crate::{
1314
};
1415

1516
#[derive(Debug, Serialize, Deserialize, Clone)]
16-
pub struct SignerConfig {
17-
/// Docker image of the module
18-
#[serde(default = "default_signer")]
19-
pub docker_image: String,
20-
/// Which keys to load
21-
pub loader: SignerLoader,
22-
/// How to store keys
23-
pub store: Option<ProxyStore>,
17+
#[serde(rename_all = "snake_case")]
18+
pub enum SignerConfig {
19+
/// Local signer module
20+
Local {
21+
/// Docker image of the module
22+
#[serde(default = "default_signer")]
23+
docker_image: String,
24+
/// Which keys to load
25+
loader: SignerLoader,
26+
/// How to store keys
27+
store: Option<ProxyStore>,
28+
},
29+
/// Remote signer module with compatible API
30+
Remote {
31+
/// Complete url of the base API endpoint
32+
url: Url,
33+
},
2434
}
2535

2636
fn default_signer() -> String {
@@ -43,14 +53,12 @@ impl StartSignerConfig {
4353
let jwts = load_jwts()?;
4454
let server_port = load_env_var(SIGNER_PORT_ENV)?.parse()?;
4555

46-
let signer_config = config.signer.expect("Signer config is missing");
47-
48-
Ok(StartSignerConfig {
49-
chain: config.chain,
50-
loader: signer_config.loader,
51-
server_port,
52-
jwts,
53-
store: signer_config.store,
54-
})
56+
match config.signer {
57+
Some(SignerConfig::Local { loader, store, .. }) => {
58+
Ok(StartSignerConfig { chain: config.chain, loader, server_port, jwts, store })
59+
}
60+
Some(SignerConfig::Remote { .. }) => bail!("Remote signer configured"),
61+
None => bail!("Signer config is missing"),
62+
}
5563
}
5664
}

docs/docs/get_started/configuration.md

+21-9
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,20 @@ Note that in this setup, the signer module will not be started.
3131

3232
## Signer module
3333

34-
To start the signer module, you need to include its parameters in the config file:
34+
Commit-Boost supports both local and remote signers. The signer module is responsible for signing the transactions that other modules generates. Please note that only one signer at a time is allowed.
35+
36+
### Local signer
37+
38+
To start a local signer module, you need to include its parameters in the config file
3539

3640
```toml
37-
[signer]
38-
[signer.loader]
41+
[signer.local.loader]
3942
format = "lighthouse"
4043
keys_path = "/path/to/keys"
4144
secrets_path = "/path/to.secrets"
4245
```
4346

44-
We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's easier to load the keys. We're working on adding support for additional keystores, including remote signers. These are the expected file structures for each format:
47+
We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's easier to load the keys. We're working on adding support for additional keystores. These are the expected file structures for each format:
4548

4649
<details>
4750
<summary>Lighthouse</summary>
@@ -61,7 +64,7 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea
6164
#### Config:
6265
```toml
6366
[signer]
64-
[signer.loader]
67+
[signer.local.loader]
6568
format = "lighthouse"
6669
keys_path = "keys"
6770
secrets_path = "secrets"
@@ -84,7 +87,7 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea
8487
#### Config:
8588
```toml
8689
[signer]
87-
[signer.loader]
90+
[signer.local.loader]
8891
format = "prysm"
8992
keys_path = "wallet/direct/accounts/all-accounts.keystore.json"
9093
secrets_path = "secrets/password.txt"
@@ -107,7 +110,7 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea
107110
#### Config:
108111
```toml
109112
[signer]
110-
[signer.loader]
113+
[signer.local.loader]
111114
format = "teku"
112115
keys_path = "keys"
113116
secrets_path = "secrets"
@@ -128,8 +131,7 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea
128131

129132
#### Config:
130133
```toml
131-
[signer]
132-
[signer.loader]
134+
[signer.local.loader]
133135
format = "lodestar"
134136
keys_path = "keys"
135137
secrets_path = "secrets/password.txt"
@@ -140,6 +142,16 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea
140142
:::
141143
</details>
142144

145+
### Remote signer
146+
147+
You might choose to use an external service to sign the transactions. For now, we support Web3Signer but we're working on adding support for additional signers.
148+
149+
The parameters needed for the remote signer are:
150+
151+
```toml
152+
[signer.remote]
153+
url = "https://remote.signer.url"
154+
```
143155

144156
## Custom module
145157
We currently provide a test module that needs to be built locally. To build the module run:

0 commit comments

Comments
 (0)