Skip to content

Commit 0ac4008

Browse files
sambachaltitanbjclapisManuelBilbao
authored
Rc4 (#7)
* bump version * Successful cross-compilation, but runtime has memory allocation issues * Working with OpenSSL static-linked * Got dynamic linking working, added a feature flag to toggle dynamic vs. static * Fixed the vendored build arg * Reintroduced the cargo chef setup * Ported the cross-compilation stuff into PBS * Split the dockerfiles into separate builder / image definitions * Added a build guide * Refactored the Github release action to use the Docker builder * Fixed the Docker image binary filenames * Cleaned up the Darwin artifact step * Made the CI workflow and justfile use the same toolchain as the source * Revert "Made the CI workflow and justfile use the same toolchain as the source" This reverts commit 58c6117. * Testing removal of OpenSSL vendored option * Updating just in the CI workflow * Refactored the signer to support host and port config settings * Updated docs * Fixing Clippy in CI workflow * Removed obviated CI setup * Minor dedup of RwLock guard acquisition * Added rate limiting for signer clients with repeated JWT auth failures * Added Signer config validation * Started unit test setup for the Signer * Finished a basic signer module unit test * Added a JWT failure unit test * Added a rate limit test and cleaned up a bit * Added unique ports to unit tests for parallel execution * Cleaned up the build Dockerfile and removed an extra dependency layer * Ported the build script over to the justfile * Added a justfile recipe for installing protoc * Update crates/cli/src/docker_init.rs Co-authored-by: ltitanb <[email protected]> * Added example signer config params * Cleaned up signer config loading from feedback * Added JWT auth fields to the example config * Started building the JWT config file * Added tests * Started migration from JWTS_ENV to the config file * Signing requests now uses the module's signing ID * Finished added signing ID support and a quick test * Fixed some example config parameters * Added a test to ensure modules can't create the same sigs * Made the jwt_config_file optional * Started working on docs * Redid implementation with the original JWTS env var * Started the signer doc * Overhauled the signing_id setup to be directly in the signed struct * Made proposer commitments nested Merkle trees to allow Dirk support * Added the signer request guide * Added quotes to some HTML * Added some simple JWT secret info * Adding a closing tag * Added prop commit signature verification helpers for modules to use * Fixed some params in da_commit * Cleaned load_module_signing_configs a bit * Fixed some docs language * Refactored into compute_prop_commit_signing_root * CBST2-04: Update JWT secrets on reload and revoke module endpoint (Commit-Boost#295) * Signing IDs are no longer optional in the config * Refactored some of the signer consts for consistency * Updated the Signer API docs * Merge sigp-audit-fixes (Commit-Boost#348) Co-authored-by: Manuel Iñaki Bilbao <[email protected]> Co-authored-by: ltitanb <[email protected]> * Move from [u8; 32] to B256 everywhere (Commit-Boost#347) * Cleaned up some hashmap usage * Removed compute_tree_hash_root() * Some minor cleanup * Fixed some docs --------- Co-authored-by: eltitanb <[email protected]> Co-authored-by: Joe Clapis <[email protected]> Co-authored-by: ltitanb <[email protected]> Co-authored-by: Manuel Iñaki Bilbao <[email protected]>
1 parent 7d32afb commit 0ac4008

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1549
-293
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/signer-api.yml

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ paths:
6060

6161
/signer/v1/request_signature:
6262
post:
63-
summary: Send a signature request
63+
summary: Request a signature for a 32-byte blob of data (typically a hash), signed by the requested BLS or ECDSA key.
6464
tags:
6565
- Signer
6666
security:
@@ -81,15 +81,15 @@ paths:
8181
type: string
8282
enum: [consensus, proxy_bls, proxy_ecdsa]
8383
pubkey:
84-
description: Public key of the validator for consensus signatures
84+
description: The 48-byte BLS public key, with optional `0x` prefix, of the proposer key that you want to request a signature from.
8585
$ref: "#/components/schemas/BlsPubkey"
8686
proxy:
87-
description: BLS proxy pubkey or ECDSA address for proxy signatures
87+
description: The 48-byte BLS public key (for `proxy_bls` mode) or the 20-byte Ethereum address (for `proxy_ecdsa` mode), with optional `0x` prefix, of the proxy key that you want to request a signature from.
8888
oneOf:
8989
- $ref: "#/components/schemas/BlsPubkey"
9090
- $ref: "#/components/schemas/EcdsaAddress"
9191
object_root:
92-
description: The root of the object to be signed
92+
description: The 32-byte data you want to sign, with optional `0x` prefix.
9393
type: string
9494
format: hex
9595
pattern: "^0x[a-fA-F0-9]{64}$"
@@ -112,7 +112,7 @@ paths:
112112
object_root: "0x3e9f4a78b5c21d64f0b8e3d9a7f5c02b4d1e67a3c8f29b5d6e4a3b1c8f72e6d9"
113113
responses:
114114
"200":
115-
description: Success
115+
description: A successful signature response. The returned signature is the Merkle root hash of the provided `object_root` field and the requesting module's Signing ID as specified in the Commit-Boost configuration. For details on this signature, see the [signature structure documentation](https://commit-boost.github.io/commit-boost-client/developing/prop-commit-signing.md#structure-of-a-signature).
116116
content:
117117
application/json:
118118
schema:
@@ -126,8 +126,45 @@ paths:
126126
value: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989a3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
127127
ProxyEcdsa:
128128
value: "0x985b495f49d1b96db3bba3f6c5dd1810950317c10d4c2042bd316f338cdbe74359072e209b85e56ac492092d7860063dd096ca31b4e164ef27e3f8d508e656801c"
129+
"400":
130+
description: |
131+
This can occur in several scenarios:
132+
133+
- You requested an operation while using the Dirk signer mode instead of locally-managed signer mode, but Dirk doesn't support that operation.
134+
- Something went wrong while preparing your request; the error text will provide more information.
135+
content:
136+
application/json:
137+
schema:
138+
type: object
139+
required:
140+
- code
141+
- message
142+
properties:
143+
code:
144+
type: number
145+
example: 400
146+
message:
147+
type: string
148+
example: "Bad request: Invalid pubkey format"
149+
"401":
150+
description: The requesting module did not provide a JWT string in the request's authorization header, or the JWT string was not configured in the signer service's configuration file as belonging to the module.
151+
content:
152+
application/json:
153+
schema:
154+
type: object
155+
required:
156+
- code
157+
- message
158+
properties:
159+
code:
160+
type: number
161+
example: 401
162+
message:
163+
type: string
164+
example: "Unauthorized"
165+
129166
"404":
130-
description: Unknown value (pubkey, etc.)
167+
description: You either requested a route that doesn't exist, or you requested a signature from a key that does not exist.
131168
content:
132169
application/json:
133170
schema:
@@ -142,8 +179,24 @@ paths:
142179
message:
143180
type: string
144181
example: "Unknown pubkey"
182+
"429":
183+
description: Your module attempted and failed JWT authentication too many times recently, and is currently timed out. It cannot make any more requests until the timeout ends.
184+
content:
185+
application/json:
186+
schema:
187+
type: object
188+
required:
189+
- code
190+
- message
191+
properties:
192+
code:
193+
type: number
194+
example: 429
195+
message:
196+
type: string
197+
example: "Too many requests"
145198
"500":
146-
description: Internal error
199+
description: Your request was valid, but something went wrong internally that prevented it from being fulfilled.
147200
content:
148201
application/json:
149202
schema:
@@ -158,6 +211,22 @@ paths:
158211
message:
159212
type: string
160213
example: "Internal error"
214+
"502":
215+
description: The signer service is running in Dirk signer mode, but Dirk could not be reached.
216+
content:
217+
application/json:
218+
schema:
219+
type: object
220+
required:
221+
- code
222+
- message
223+
properties:
224+
code:
225+
type: number
226+
example: 502
227+
message:
228+
type: string
229+
example: "Bad gateway: Dirk signer service is unreachable"
161230

162231
/signer/v1/generate_proxy_key:
163232
post:

bin/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ pub mod prelude {
1010
load_pbs_custom_config, LogsSettings, StartCommitModuleConfig, PBS_MODULE_NAME,
1111
},
1212
pbs::{BuilderEvent, BuilderEventClient, OnBuilderApiEvent},
13+
signature::{
14+
verify_proposer_commitment_signature_bls, verify_proposer_commitment_signature_ecdsa,
15+
},
1316
signer::{BlsPublicKey, BlsSignature, EcdsaSignature},
1417
types::Chain,
1518
utils::{initialize_tracing_log, utcnow_ms, utcnow_ns, utcnow_sec, utcnow_us},

config.example.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ url = "http://0xa119589bb33ef52acbb8116832bec2b58fca590fe5c85eac5d3230b44d5bc09f
152152
# - Dirk: a remote Dirk instance
153153
# - Local: a local Signer module
154154
# More details on the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/#signer-module)
155-
# [signer]
155+
[signer]
156156
# Docker image to use for the Signer module.
157157
# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest
158-
# docker_image = "ghcr.io/commit-boost/signer:latest"
158+
docker_image = "ghcr.io/commit-boost/signer:latest"
159159
# Host to bind the Signer API server to
160160
# OPTIONAL, DEFAULT: 127.0.0.1
161161
host = "127.0.0.1"
@@ -249,6 +249,8 @@ proxy_dir = "./proxies"
249249
[[modules]]
250250
# Unique ID of the module
251251
id = "DA_COMMIT"
252+
# Unique hash that the Signer service will combine with the incoming data in signing requests to generate a signature specific to this module
253+
signing_id = "0x6a33a23ef26a4836979edff86c493a69b26ccf0b4a16491a815a13787657431b"
252254
# Type of the module. Supported values: commit, events
253255
type = "commit"
254256
# Docker image of the module

crates/cli/src/docker_init.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ use std::{
66

77
use cb_common::{
88
config::{
9-
CommitBoostConfig, LogsSettings, ModuleKind, SignerConfig, SignerType, BUILDER_PORT_ENV,
10-
BUILDER_URLS_ENV, CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV, DIRK_CA_CERT_DEFAULT,
11-
DIRK_CA_CERT_ENV, DIRK_CERT_DEFAULT, DIRK_CERT_ENV, DIRK_DIR_SECRETS_DEFAULT,
12-
DIRK_DIR_SECRETS_ENV, DIRK_KEY_DEFAULT, DIRK_KEY_ENV, JWTS_ENV, LOGS_DIR_DEFAULT,
13-
LOGS_DIR_ENV, METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_ENDPOINT_ENV,
14-
PBS_MODULE_NAME, PROXY_DIR_DEFAULT, PROXY_DIR_ENV, PROXY_DIR_KEYS_DEFAULT,
15-
PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_DEFAULT, PROXY_DIR_SECRETS_ENV, SIGNER_DEFAULT,
16-
SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_DEFAULT,
17-
SIGNER_DIR_SECRETS_ENV, SIGNER_ENDPOINT_ENV, SIGNER_KEYS_ENV, SIGNER_MODULE_NAME,
18-
SIGNER_PORT_DEFAULT, SIGNER_URL_ENV,
9+
CommitBoostConfig, LogsSettings, ModuleKind, SignerConfig, SignerType, ADMIN_JWT_ENV,
10+
BUILDER_PORT_ENV, BUILDER_URLS_ENV, CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV,
11+
DIRK_CA_CERT_DEFAULT, DIRK_CA_CERT_ENV, DIRK_CERT_DEFAULT, DIRK_CERT_ENV,
12+
DIRK_DIR_SECRETS_DEFAULT, DIRK_DIR_SECRETS_ENV, DIRK_KEY_DEFAULT, DIRK_KEY_ENV, JWTS_ENV,
13+
LOGS_DIR_DEFAULT, LOGS_DIR_ENV, METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV,
14+
PBS_ENDPOINT_ENV, PBS_MODULE_NAME, PROXY_DIR_DEFAULT, PROXY_DIR_ENV,
15+
PROXY_DIR_KEYS_DEFAULT, PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_DEFAULT,
16+
PROXY_DIR_SECRETS_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV,
17+
SIGNER_DIR_SECRETS_DEFAULT, SIGNER_DIR_SECRETS_ENV, SIGNER_ENDPOINT_ENV, SIGNER_KEYS_ENV,
18+
SIGNER_MODULE_NAME, SIGNER_PORT_DEFAULT, SIGNER_URL_ENV,
1919
},
2020
pbs::{BUILDER_V1_API_PATH, GET_STATUS_PATH},
2121
signer::{ProxyStore, SignerLoader},
@@ -333,6 +333,7 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re
333333
let mut signer_envs = IndexMap::from([
334334
get_env_val(CONFIG_ENV, CONFIG_DEFAULT),
335335
get_env_same(JWTS_ENV),
336+
get_env_same(ADMIN_JWT_ENV),
336337
]);
337338

338339
// Bind the signer API to 0.0.0.0
@@ -366,6 +367,7 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re
366367

367368
// write jwts to env
368369
envs.insert(JWTS_ENV.into(), format_comma_separated(&jwts));
370+
envs.insert(ADMIN_JWT_ENV.into(), random_jwt_secret());
369371

370372
// volumes
371373
let mut volumes = vec![config_volume.clone()];

crates/common/src/commit/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature";
33
pub const GENERATE_PROXY_KEY_PATH: &str = "/signer/v1/generate_proxy_key";
44
pub const STATUS_PATH: &str = "/status";
55
pub const RELOAD_PATH: &str = "/reload";
6+
pub const REVOKE_MODULE_PATH: &str = "/revoke_jwt";

crates/common/src/commit/request.rs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
use std::{
2+
collections::HashMap,
23
fmt::{self, Debug, Display},
34
str::FromStr,
45
};
56

67
use alloy::{
78
hex,
8-
primitives::{Address, B256},
9+
primitives::{aliases::B32, Address, B256},
910
rpc::types::beacon::BlsSignature,
1011
};
1112
use derive_more::derive::From;
12-
use serde::{Deserialize, Serialize};
13+
use serde::{Deserialize, Deserializer, Serialize};
1314
use tree_hash::TreeHash;
1415
use tree_hash_derive::TreeHash;
1516

1617
use crate::{
17-
constants::COMMIT_BOOST_DOMAIN, error::BlstErrorWrapper, signature::verify_signed_message,
18-
signer::BlsPublicKey, types::Chain,
18+
config::decode_string_to_map,
19+
constants::COMMIT_BOOST_DOMAIN,
20+
error::BlstErrorWrapper,
21+
signature::verify_signed_message,
22+
signer::BlsPublicKey,
23+
types::{Chain, ModuleId},
1924
};
2025

2126
pub trait ProxyId: AsRef<[u8]> + Debug + Clone + Copy + TreeHash + Display {}
@@ -57,7 +62,8 @@ impl<T: ProxyId> SignedProxyDelegation<T> {
5762
&self.message.delegator,
5863
&self.message,
5964
&self.signature,
60-
COMMIT_BOOST_DOMAIN,
65+
None,
66+
&B32::from(COMMIT_BOOST_DOMAIN),
6167
)
6268
}
6369
}
@@ -198,6 +204,31 @@ pub struct GetPubkeysResponse {
198204
pub keys: Vec<ConsensusProxyMap>,
199205
}
200206

207+
#[derive(Debug, Clone, Serialize, Deserialize)]
208+
pub struct ReloadRequest {
209+
#[serde(default, deserialize_with = "deserialize_jwt_secrets")]
210+
pub jwt_secrets: Option<HashMap<ModuleId, String>>,
211+
pub admin_secret: Option<String>,
212+
}
213+
214+
pub fn deserialize_jwt_secrets<'de, D>(
215+
deserializer: D,
216+
) -> Result<Option<HashMap<ModuleId, String>>, D::Error>
217+
where
218+
D: Deserializer<'de>,
219+
{
220+
let raw: String = Deserialize::deserialize(deserializer)?;
221+
222+
decode_string_to_map(&raw)
223+
.map(Some)
224+
.map_err(|_| serde::de::Error::custom("Invalid format".to_string()))
225+
}
226+
227+
#[derive(Debug, Clone, Serialize, Deserialize)]
228+
pub struct RevokeModuleRequest {
229+
pub module_id: ModuleId,
230+
}
231+
201232
/// Map of consensus pubkeys to proxies
202233
#[derive(Debug, Clone, Deserialize, Serialize)]
203234
pub struct ConsensusProxyMap {
@@ -288,7 +319,7 @@ mod tests {
288319

289320
let _: SignedProxyDelegationBls = serde_json::from_str(data).unwrap();
290321

291-
let data = r#"{
322+
let data = r#"{
292323
"message": {
293324
"delegator": "0xa3366b54f28e4bf1461926a3c70cdb0ec432b5c92554ecaae3742d33fb33873990cbed1761c68020e6d3c14d30a22050",
294325
"proxy": "0x4ca9939a8311a7cab3dde201b70157285fa81a9d"

crates/common/src/config/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub const SIGNER_JWT_AUTH_FAIL_TIMEOUT_SECONDS_DEFAULT: u32 = 5 * 60;
4747

4848
/// Comma separated list module_id=jwt_secret
4949
pub const JWTS_ENV: &str = "CB_JWTS";
50+
pub const ADMIN_JWT_ENV: &str = "CB_SIGNER_ADMIN_JWT";
5051

5152
/// Path to json file with plaintext keys (testing only)
5253
pub const SIGNER_KEYS_ENV: &str = "CB_SIGNER_LOADER_FILE";

crates/common/src/config/module.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashMap;
22

3+
use alloy::primitives::B256;
34
use eyre::{ContextCompat, Result};
45
use serde::{de::DeserializeOwned, Deserialize, Serialize};
56
use toml::Table;
@@ -37,6 +38,8 @@ pub struct StaticModuleConfig {
3738
/// Type of the module
3839
#[serde(rename = "type")]
3940
pub kind: ModuleKind,
41+
/// Signing ID for the module to use when requesting signatures
42+
pub signing_id: B256,
4043
}
4144

4245
/// Runtime config to start a module

0 commit comments

Comments
 (0)