Skip to content

[Merged by Bors] - Web3Signer support for VC #2522

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

Closed
wants to merge 81 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
4e527a7
Add basic remote signer format
paulhauner Aug 18, 2021
82251c8
Add support to validator store
paulhauner Aug 18, 2021
02fc12c
Create signing_method.rs
paulhauner Aug 18, 2021
900ad73
Fix aggregate signing
paulhauner Aug 19, 2021
743587c
Add basic request types
paulhauner Aug 19, 2021
017e56f
Use references for request types
paulhauner Aug 19, 2021
be9d095
Update validator_store to pre-image
paulhauner Aug 19, 2021
24be60c
Add default remote signer timeout
paulhauner Aug 19, 2021
87063fd
Add calls to http server
paulhauner Aug 19, 2021
4dfd853
Refactor task executor metrics
paulhauner Aug 20, 2021
5824332
Partially complete async refactor
paulhauner Aug 20, 2021
675207e
Refactor aggregator proofs
paulhauner Aug 26, 2021
3846e10
Remove code comments
paulhauner Aug 26, 2021
535442b
Partially address clippy lints
paulhauner Aug 26, 2021
86d4824
Add signing context
paulhauner Aug 26, 2021
b6c53e2
Ignore clippy lint
paulhauner Aug 26, 2021
5cb0ad0
Rename Web3Signer to remote signer
paulhauner Aug 26, 2021
2520695
Fix tests
paulhauner Aug 26, 2021
5f3d679
Add unimplemented Altair block handler
paulhauner Aug 26, 2021
c566418
Fix doppelganger check
paulhauner Aug 26, 2021
3912cd8
Add build script for tests
paulhauner Aug 26, 2021
34cdee7
Add tests
paulhauner Aug 26, 2021
8c49558
Add basic web3 signer rig
paulhauner Aug 27, 2021
99157d7
Add missed await
paulhauner Aug 27, 2021
af73fe6
Add validator store rig
paulhauner Aug 27, 2021
f532f18
Add TestingRig
paulhauner Aug 31, 2021
3e53132
Add first working test
paulhauner Aug 31, 2021
88f2481
Fix unused vars, tidy
paulhauner Aug 31, 2021
17a156c
Add block test
paulhauner Aug 31, 2021
6c9f0e9
Add integration test to CI
paulhauner Aug 31, 2021
da7e027
Fix integration test
paulhauner Aug 31, 2021
7845747
Block test on cargo-fmt
paulhauner Aug 31, 2021
7f8e23b
Add more tests, increase upcheck timeout
paulhauner Aug 31, 2021
3462bbf
Add more tests
paulhauner Aug 31, 2021
df7eaa1
Fix u64 quoting issue
paulhauner Aug 31, 2021
65f6b7f
Separate tests
paulhauner Aug 31, 2021
b7b1d34
Add Altair tests for all networks
paulhauner Aug 31, 2021
6521d20
Fix clippy lints
paulhauner Aug 31, 2021
e855be4
Move web3signer structs into their own file
paulhauner Aug 31, 2021
ff750f4
Add SignableMessage
paulhauner Aug 31, 2021
8c47ad8
Add docs
paulhauner Aug 31, 2021
1fca268
Add method to API
paulhauner Sep 3, 2021
68c31a2
Add basic test
paulhauner Sep 3, 2021
e605fee
Rename serde_utils
paulhauner Sep 5, 2021
ee8dc98
Add docs for HTTP API
paulhauner Sep 5, 2021
d41d40f
Tidy
paulhauner Sep 5, 2021
b90bb18
Tidy web3signer tests
paulhauner Sep 6, 2021
1eff618
Tidy, add comments
paulhauner Sep 6, 2021
6b0037a
Use read lock instead of write lock
paulhauner Sep 6, 2021
7ba8edc
Tidy
paulhauner Sep 6, 2021
fa3a023
Remove domain mapping
paulhauner Sep 6, 2021
8856736
Use with_capacity for vecs
paulhauner Sep 6, 2021
13791e4
Add signing context function
paulhauner Sep 6, 2021
a4c05e0
Allow for fixed web3signer version
paulhauner Sep 6, 2021
58b05b8
Tidy
paulhauner Sep 6, 2021
c258948
Add altair block support
paulhauner Sep 6, 2021
d2d8106
Add altair block tests
paulhauner Sep 6, 2021
fd829a6
Use cfg flags to bar windows
paulhauner Sep 6, 2021
8185308
Remove repeated web3signer test from CI
paulhauner Sep 6, 2021
0ff6e7b
Still download artifacts on windows
paulhauner Sep 6, 2021
00e6600
Use tls in testing
paulhauner Sep 6, 2021
966efcc
Fix build script
paulhauner Sep 6, 2021
19674f6
Fix import
paulhauner Sep 6, 2021
f1b1d2f
Check to see if dir exists in build script
paulhauner Sep 6, 2021
a7128cc
Tidy web3signer types
paulhauner Sep 6, 2021
d551458
Use latest release for testing
paulhauner Sep 12, 2021
3e34297
Add comment to build.rs
paulhauner Sep 13, 2021
b1a5237
Apply suggestions from code review
paulhauner Sep 13, 2021
c4b6f55
Log error
paulhauner Sep 14, 2021
9f3919c
Suppress web3signer logs
paulhauner Sep 14, 2021
d083ef1
Create DutyAndProof in parallel
paulhauner Sep 14, 2021
dfb3ce8
Perform sync committee tasks in parallel
paulhauner Sep 14, 2021
12db2ce
Perform sync proof signatures in parallel
paulhauner Sep 14, 2021
a6fa121
Produce attestation sigs in parallel
paulhauner Sep 15, 2021
6d0b725
Produce aggregates in parallel
paulhauner Sep 15, 2021
c6ca374
Tidy sync committee service
paulhauner Sep 15, 2021
d1b3c77
Tidy sync
paulhauner Sep 15, 2021
fbe17e4
Fix web3signer test
paulhauner Sep 15, 2021
5d0d494
Add pubkey to log
paulhauner Sep 15, 2021
f792417
Merge branch 'unstable' into remote-signer-client
paulhauner Sep 15, 2021
93f41a2
Add signing times metrics
paulhauner Sep 16, 2021
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
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ members = [
"testing/node_test_rig",
"testing/simulator",
"testing/state_transition_vectors",
"testing/web3signer_tests",

"validator_client",
"validator_client/slashing_protection",
Expand Down
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* [Advanced Usage](./advanced.md)
* [Custom Data Directories](./advanced-datadir.md)
* [Validator Graffiti](./graffiti.md)
* [Remote Signing with Web3Signer](./validator-web3signer.md)
* [Database Configuration](./advanced_database.md)
* [Advanced Networking](./advanced_networking.md)
* [Running a Slasher](./slasher.md)
Expand Down
40 changes: 40 additions & 0 deletions book/src/api-vc-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,43 @@ Typical Responses | 200
]
}
```

## `POST /lighthouse/validators/web3signer`

Create any number of new validators, all of which will refer to a
[Web3Signer](https://docs.web3signer.consensys.net/en/latest/) server for signing.

### HTTP Specification

| Property | Specification |
| --- |--- |
Path | `/lighthouse/validators/web3signer`
Method | POST
Required Headers | [`Authorization`](./api-vc-auth-header.md)
Typical Responses | 200, 400

### Example Request Body

```json
[
{
"enable": true,
"description": "validator_one",
"graffiti": "Mr F was here",
"voting_public_key": "0xa062f95fee747144d5e511940624bc6546509eeaeae9383257a9c43e7ddc58c17c2bab4ae62053122184c381b90db380",
"url": "http://path-to-web3signer.com",
"root_certificate_path": "/path/on/vc/filesystem/to/certificate.pem",
"request_timeout_ms": 12000
}
]
```

The following fields may be omitted or nullified to obtain default values:

- `graffiti`
- `root_certificate_path`
- `request_timeout_ms`

### Example Response Body

*No data is included in the response body.*
56 changes: 56 additions & 0 deletions book/src/validator-web3signer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Remote Signing with Web3Signer

[Web3Signer]: https://docs.web3signer.consensys.net/en/latest/
[Consensys]: https://github.com/ConsenSys/
[Teku]: https://github.com/consensys/teku

[Web3Signer] is a tool by Consensys which allows *remote signing*. Remote signing is when a
Validator Client (VC) out-sources the signing of messages to remote server (e.g., via HTTPS). This
means that the VC does not hold the validator private keys.

## Warnings

Using a remote signer comes with risks, please read the following two warnings before proceeding:

### Remote signing is complex and risky

Remote signing is generally only desirable for enterprise users or users with unique security
requirements. Most users will find the separation between the Beacon Node (BN) and VC to be
sufficient *without* introducing a remote signer.

**Using a remote signer introduces a new set of security and slashing risks and should only be
undertaken by advanced users who fully understand the risks.**

### Web3Signer is not maintained by Lighthouse

The [Web3Signer] tool is maintained by [Consensys], the same team that maintains [Teku]. The
Lighthouse team (Sigma Prime) does not maintain Web3Signer or make any guarantees about its safety
or effectiveness.

## Usage

A remote signing validator is added to Lighthouse in much the same way as one that uses a local
keystore, via the [`validator_definitions.yml`](./validator-management.md) file or via the `POST
/lighthouse/validators/web3signer` API endpoint.

Here is an example of a `validator_definitions.yml` file containing one validator which uses a
remote signer:

```yaml
---
- enabled: true
voting_public_key: "0xa5566f9ec3c6e1fdf362634ebec9ef7aceb0e460e5079714808388e5d48f4ae1e12897fed1bea951c17fa389d511e477"
type: web3signer
url: "https://my-remote-signer.com:1234"
root_certificate_path: /home/paul/my-certificates/my-remote-signer.pem
```

When using this file, the Lighthouse VC will perform duties for the `0xa5566..` validator and defer
to the `https://my-remote-signer.com:1234` server to obtain any signatures. It will load a
"self-signed" SSL certificate from `/home/paul/my-certificates/my-remote-signer.pem` (on the
filesystem of the VC) to encrypt the communications between the VC and Web3Signer.

> The `request_timeout_ms` key can also be specified. Use this key to override the default timeout
> with a new timeout in milliseconds. This is the timeout before requests to Web3Signer are
> considered to be failures. Setting a value that it too-long may create contention and late duties
> in the VC. Setting it too short will result in failed signatures and therefore missed duties.
27 changes: 25 additions & 2 deletions common/account_utils/src/validator_definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ pub enum SigningDefinition {
#[serde(skip_serializing_if = "Option::is_none")]
voting_keystore_password: Option<ZeroizeString>,
},
/// A validator that defers to a Web3Signer HTTP server for signing.
///
/// https://github.com/ConsenSys/web3signer
#[serde(rename = "web3signer")]
Web3Signer {
url: String,
/// Path to a .pem file.
#[serde(skip_serializing_if = "Option::is_none")]
root_certificate_path: Option<PathBuf>,
/// Specifies a request timeout.
///
/// The timeout is applied from when the request starts connecting until the response body has finished.
#[serde(skip_serializing_if = "Option::is_none")]
request_timeout_ms: Option<u64>,
},
}

/// A validator that may be initialized by this validator client.
Expand Down Expand Up @@ -116,6 +131,12 @@ impl ValidatorDefinition {
#[derive(Default, Serialize, Deserialize)]
pub struct ValidatorDefinitions(Vec<ValidatorDefinition>);

impl From<Vec<ValidatorDefinition>> for ValidatorDefinitions {
fn from(vec: Vec<ValidatorDefinition>) -> Self {
Self(vec)
}
}

impl ValidatorDefinitions {
/// Open an existing file or create a new, empty one if it does not exist.
pub fn open_or_create<P: AsRef<Path>>(validators_dir: P) -> Result<Self, Error> {
Expand Down Expand Up @@ -167,11 +188,13 @@ impl ValidatorDefinitions {
let known_paths: HashSet<&PathBuf> = self
.0
.iter()
.map(|def| match &def.signing_definition {
.filter_map(|def| match &def.signing_definition {
SigningDefinition::LocalKeystore {
voting_keystore_path,
..
} => voting_keystore_path,
} => Some(voting_keystore_path),
// A Web3Signer validator does not use a local keystore file.
SigningDefinition::Web3Signer { .. } => None,
})
.collect();

Expand Down
16 changes: 16 additions & 0 deletions common/eth2/src/lighthouse_vc/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,22 @@ impl ValidatorClientHttpClient {
self.post(path, &request).await
}

/// `POST lighthouse/validators/web3signer`
pub async fn post_lighthouse_validators_web3signer(
&self,
request: &[Web3SignerValidatorRequest],
) -> Result<GenericResponse<ValidatorData>, Error> {
let mut path = self.server.full.clone();

path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("lighthouse")
.push("validators")
.push("web3signer");

self.post(path, &request).await
}

/// `PATCH lighthouse/validators/{validator_pubkey}`
pub async fn patch_lighthouse_validators(
&self,
Expand Down
18 changes: 18 additions & 0 deletions common/eth2/src/lighthouse_vc/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use account_utils::ZeroizeString;
use eth2_keystore::Keystore;
use graffiti::GraffitiString;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

pub use crate::lighthouse::Health;
pub use crate::types::{GenericResponse, VersionData};
Expand Down Expand Up @@ -64,3 +65,20 @@ pub struct KeystoreValidatorsPostRequest {
pub keystore: Keystore,
pub graffiti: Option<GraffitiString>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Web3SignerValidatorRequest {
pub enable: bool,
pub description: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub graffiti: Option<GraffitiString>,
pub voting_public_key: PublicKey,
pub url: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub root_certificate_path: Option<PathBuf>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub request_timeout_ms: Option<u64>,
}
12 changes: 12 additions & 0 deletions common/lighthouse_metrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@ pub fn set_gauge_vec(int_gauge_vec: &Result<IntGaugeVec>, name: &[&str], value:
}
}

pub fn inc_gauge_vec(int_gauge_vec: &Result<IntGaugeVec>, name: &[&str]) {
if let Some(gauge) = get_int_gauge(int_gauge_vec, name) {
gauge.inc();
}
}

pub fn dec_gauge_vec(int_gauge_vec: &Result<IntGaugeVec>, name: &[&str]) {
if let Some(gauge) = get_int_gauge(int_gauge_vec, name) {
gauge.dec();
}
}

pub fn set_gauge(gauge: &Result<IntGauge>, value: i64) {
if let Ok(gauge) = gauge {
gauge.set(value);
Expand Down
56 changes: 25 additions & 31 deletions common/task_executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,39 +237,33 @@ impl TaskExecutor {
{
let log = self.log.clone();

if let Some(metric) = metrics::get_histogram(&metrics::BLOCKING_TASKS_HISTOGRAM, &[name]) {
if let Some(int_gauge) = metrics::get_int_gauge(&metrics::BLOCKING_TASKS_COUNT, &[name])
{
let int_gauge_1 = int_gauge;
let timer = metric.start_timer();
let join_handle = if let Some(runtime) = self.runtime.upgrade() {
runtime.spawn_blocking(task)
} else {
debug!(self.log, "Couldn't spawn task. Runtime shutting down");
return None;
};
let timer = metrics::start_timer_vec(&metrics::BLOCKING_TASKS_HISTOGRAM, &[name]);
metrics::inc_gauge_vec(&metrics::BLOCKING_TASKS_COUNT, &[name]);

Some(async move {
let result = match join_handle.await {
Ok(result) => {
trace!(log, "Blocking task completed"; "task" => name);
Ok(result)
}
Err(e) => {
debug!(log, "Blocking task ended unexpectedly"; "error" => %e);
Err(e)
}
};
timer.observe_duration();
int_gauge_1.dec();
result
})
} else {
None
}
let join_handle = if let Some(runtime) = self.runtime.upgrade() {
runtime.spawn_blocking(task)
} else {
None
}
debug!(self.log, "Couldn't spawn task. Runtime shutting down");
return None;
};

let future = async move {
let result = match join_handle.await {
Ok(result) => {
trace!(log, "Blocking task completed"; "task" => name);
Ok(result)
}
Err(e) => {
debug!(log, "Blocking task ended unexpectedly"; "error" => %e);
Err(e)
}
};
drop(timer);
metrics::dec_gauge_vec(&metrics::BLOCKING_TASKS_COUNT, &[name]);
result
};

Some(future)
}

pub fn runtime(&self) -> Weak<Runtime> {
Expand Down
Loading