mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 08:52:54 +00:00
[Remote signer] Fold signer into Lighthouse repository (#1852)
The remote signer relies on the `types` and `crypto/bls` crates from Lighthouse. Moreover, a number of tests of the remote signer consumption of LH leverages this very signer, making any important update a potential dependency nightmare. Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
57
remote_signer/client/src/api_error.rs
Normal file
57
remote_signer/client/src/api_error.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use hyper::{Body, Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::to_string;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum ApiError {
|
||||
ServerError(String),
|
||||
NotImplemented(String),
|
||||
BadRequest(String),
|
||||
NotFound(String),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ApiErrorDesc {
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
pub type ApiResult = Result<Response<Body>, ApiError>;
|
||||
|
||||
impl ApiError {
|
||||
pub fn status_code(self) -> (StatusCode, String) {
|
||||
match self {
|
||||
ApiError::ServerError(desc) => (StatusCode::INTERNAL_SERVER_ERROR, desc),
|
||||
ApiError::NotImplemented(desc) => (StatusCode::NOT_IMPLEMENTED, desc),
|
||||
ApiError::BadRequest(desc) => (StatusCode::BAD_REQUEST, desc),
|
||||
ApiError::NotFound(desc) => (StatusCode::NOT_FOUND, desc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Response<Body>> for ApiError {
|
||||
fn into(self) -> Response<Body> {
|
||||
let (status_code, desc) = self.status_code();
|
||||
|
||||
let json_desc = to_string(&ApiErrorDesc { error: desc })
|
||||
.expect("The struct ApiErrorDesc should always serialize.");
|
||||
|
||||
Response::builder()
|
||||
.status(status_code)
|
||||
.body(Body::from(json_desc))
|
||||
.expect("Response should always be created.")
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for ApiError {
|
||||
fn cause(&self) -> Option<&dyn StdError> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ApiError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let status = self.clone().status_code();
|
||||
write!(f, "{:?}: {:?}", status.0, status.1)
|
||||
}
|
||||
}
|
||||
18
remote_signer/client/src/api_response.rs
Normal file
18
remote_signer/client/src/api_response.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UpcheckApiResponse {
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// Contains the response to the `get_keys` API.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct KeysApiResponse {
|
||||
pub keys: Vec<String>,
|
||||
}
|
||||
|
||||
/// Contains the response to the `sign_message` API.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct SignatureApiResponse {
|
||||
pub signature: String,
|
||||
}
|
||||
70
remote_signer/client/src/backend.rs
Normal file
70
remote_signer/client/src/backend.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::api_error::ApiError;
|
||||
use crate::api_response::{KeysApiResponse, SignatureApiResponse};
|
||||
use crate::rest_api::Context;
|
||||
use crate::signing_root::get_signing_root;
|
||||
use client_backend::{BackendError, Storage};
|
||||
use hyper::Request;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::sync::Arc;
|
||||
use types::EthSpec;
|
||||
|
||||
lazy_static! {
|
||||
static ref PUBLIC_KEY_FROM_PATH_REGEX: Regex = Regex::new(r"^/[^/]+/([^/]*)").unwrap();
|
||||
}
|
||||
|
||||
/// HTTP handler to get the list of public keys in the backend.
|
||||
pub fn get_keys<E: EthSpec, S: Storage, U>(
|
||||
_: U,
|
||||
ctx: Arc<Context<E, S>>,
|
||||
) -> Result<KeysApiResponse, ApiError> {
|
||||
let keys = ctx
|
||||
.backend
|
||||
.get_keys()
|
||||
.map_err(|e| ApiError::ServerError(format!("{}", e)))?;
|
||||
|
||||
if keys.is_empty() {
|
||||
return Err(ApiError::NotFound("No keys found in storage.".to_string()));
|
||||
}
|
||||
|
||||
Ok(KeysApiResponse { keys })
|
||||
}
|
||||
|
||||
/// HTTP handler to sign a message with the requested key.
|
||||
pub fn sign_message<E: EthSpec, S: Storage>(
|
||||
req: Request<Vec<u8>>,
|
||||
ctx: Arc<Context<E, S>>,
|
||||
) -> Result<SignatureApiResponse, ApiError> {
|
||||
// Parse the request body and compute the signing root.
|
||||
let signing_root = get_signing_root::<E>(&req, ctx.spec.clone())?;
|
||||
|
||||
// This public key parameter should have been validated by the router.
|
||||
// We are just going to extract it from the request.
|
||||
let path = req.uri().path().to_string();
|
||||
|
||||
let rc = |path: &str| -> Result<String, String> {
|
||||
let caps = PUBLIC_KEY_FROM_PATH_REGEX.captures(path).ok_or("")?;
|
||||
let re_match = caps.get(1).ok_or("")?;
|
||||
Ok(re_match.as_str().to_string())
|
||||
};
|
||||
|
||||
let public_key = rc(&path).map_err(|_| {
|
||||
ApiError::BadRequest(format!("Unable to get public key from path: {:?}", path))
|
||||
})?;
|
||||
|
||||
match ctx.backend.sign_message(&public_key, signing_root) {
|
||||
Ok(signature) => Ok(SignatureApiResponse { signature }),
|
||||
|
||||
Err(BackendError::KeyNotFound(_)) => {
|
||||
Err(ApiError::NotFound(format!("Key not found: {}", public_key)))
|
||||
}
|
||||
|
||||
Err(BackendError::InvalidPublicKey(_)) => Err(ApiError::BadRequest(format!(
|
||||
"Invalid public key: {}",
|
||||
public_key
|
||||
))),
|
||||
|
||||
// Catches InvalidSecretKey, KeyMismatch and StorageError.
|
||||
Err(e) => Err(ApiError::ServerError(e.to_string())),
|
||||
}
|
||||
}
|
||||
20
remote_signer/client/src/config.rs
Normal file
20
remote_signer/client/src/config.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
/// HTTP REST API Configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// The IPv4 address the REST API HTTP server will listen on.
|
||||
pub listen_address: Ipv4Addr,
|
||||
/// The port the REST API HTTP server will listen on.
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
listen_address: Ipv4Addr::new(127, 0, 0, 1),
|
||||
port: 9000,
|
||||
}
|
||||
}
|
||||
}
|
||||
113
remote_signer/client/src/handler.rs
Normal file
113
remote_signer/client/src/handler.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::api_error::{ApiError, ApiResult};
|
||||
use crate::rest_api::Context;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
use types::EthSpec;
|
||||
|
||||
/// Provides a HTTP request handler with specific functionality.
|
||||
pub struct Handler<E: EthSpec, S: Send + Sync> {
|
||||
req: Request<()>,
|
||||
body: Body,
|
||||
ctx: Arc<Context<E, S>>,
|
||||
allow_body: bool,
|
||||
}
|
||||
|
||||
impl<E: EthSpec, S: 'static + Send + Sync> Handler<E, S> {
|
||||
/// Start handling a new request.
|
||||
pub fn new(req: Request<Body>, ctx: Arc<Context<E, S>>) -> Result<Self, ApiError> {
|
||||
let (req_parts, body) = req.into_parts();
|
||||
let req = Request::from_parts(req_parts, ());
|
||||
|
||||
Ok(Self {
|
||||
req,
|
||||
body,
|
||||
ctx,
|
||||
allow_body: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a simple static value.
|
||||
///
|
||||
/// Does not use the blocking executor.
|
||||
pub async fn static_value<V>(self, value: V) -> Result<HandledRequest<V>, ApiError> {
|
||||
// Always check and disallow a body for a static value.
|
||||
let _ = Self::get_body(self.body, false).await?;
|
||||
|
||||
Ok(HandledRequest { value })
|
||||
}
|
||||
|
||||
/// The default behaviour is to return an error if any body is supplied in the request. Calling
|
||||
/// this function disables that error.
|
||||
pub fn allow_body(mut self) -> Self {
|
||||
self.allow_body = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawns `func` on the blocking executor.
|
||||
///
|
||||
/// This method is suitable for handling long-running or intensive tasks.
|
||||
pub async fn in_blocking_task<F, V>(self, func: F) -> Result<HandledRequest<V>, ApiError>
|
||||
where
|
||||
V: Send + Sync + 'static,
|
||||
F: Fn(Request<Vec<u8>>, Arc<Context<E, S>>) -> Result<V, ApiError> + Send + Sync + 'static,
|
||||
{
|
||||
let ctx = self.ctx;
|
||||
let executor = ctx.executor.clone();
|
||||
let body = Self::get_body(self.body, self.allow_body).await?;
|
||||
let (req_parts, _) = self.req.into_parts();
|
||||
let req = Request::from_parts(req_parts, body);
|
||||
|
||||
let value = executor
|
||||
.runtime_handle()
|
||||
.spawn_blocking(move || func(req, ctx))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ApiError::ServerError(format!(
|
||||
"Failed to get blocking join handle: {}",
|
||||
e.to_string()
|
||||
))
|
||||
})??;
|
||||
|
||||
Ok(HandledRequest { value })
|
||||
}
|
||||
|
||||
/// Downloads the bytes for `body`.
|
||||
async fn get_body(body: Body, allow_body: bool) -> Result<Vec<u8>, ApiError> {
|
||||
let bytes = hyper::body::to_bytes(body)
|
||||
.await
|
||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
||||
|
||||
if !allow_body && !bytes[..].is_empty() {
|
||||
Err(ApiError::BadRequest(
|
||||
"The request body must be empty".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(bytes.into_iter().collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A request that has been "handled" and now a result (`value`) needs to be serialized and
|
||||
/// returned.
|
||||
pub struct HandledRequest<V> {
|
||||
value: V,
|
||||
}
|
||||
|
||||
impl<V: Serialize> HandledRequest<V> {
|
||||
/// Suitable for items which only implement `serde`.
|
||||
pub fn serde_encodings(self) -> ApiResult {
|
||||
let body = Body::from(serde_json::to_string(&self.value).map_err(|e| {
|
||||
ApiError::ServerError(format!(
|
||||
"Unable to serialize response body as JSON: {:?}",
|
||||
e
|
||||
))
|
||||
})?);
|
||||
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header("content-type", "application/json")
|
||||
.body(body)
|
||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||
}
|
||||
}
|
||||
58
remote_signer/client/src/lib.rs
Normal file
58
remote_signer/client/src/lib.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
pub mod api_error;
|
||||
pub mod api_response;
|
||||
|
||||
mod backend;
|
||||
mod config;
|
||||
mod handler;
|
||||
mod rest_api;
|
||||
mod router;
|
||||
mod signing_root;
|
||||
mod upcheck;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use client_backend::Backend;
|
||||
use config::Config;
|
||||
use environment::RuntimeContext;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::SocketAddr;
|
||||
use types::EthSpec;
|
||||
|
||||
pub struct Client {
|
||||
listening_address: SocketAddr,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub async fn new<E: EthSpec>(
|
||||
context: RuntimeContext<E>,
|
||||
cli_args: &ArgMatches<'_>,
|
||||
) -> Result<Self, String> {
|
||||
let log = context.executor.log();
|
||||
|
||||
let mut config = Config::default();
|
||||
|
||||
if let Some(address) = cli_args.value_of("listen-address") {
|
||||
config.listen_address = address
|
||||
.parse::<Ipv4Addr>()
|
||||
.map_err(|_| "listen-address is not a valid IPv4 address.")?;
|
||||
}
|
||||
|
||||
if let Some(port) = cli_args.value_of("port") {
|
||||
config.port = port
|
||||
.parse::<u16>()
|
||||
.map_err(|_| "port is not a valid u16.")?;
|
||||
}
|
||||
|
||||
let backend = Backend::new(cli_args, log)?;
|
||||
|
||||
// It is useful to get the listening address if you have set up your port to be 0.
|
||||
let listening_address =
|
||||
rest_api::start_server(context.executor, config, backend, context.eth_spec_instance)
|
||||
.map_err(|e| format!("Failed to start HTTP API: {:?}", e))?;
|
||||
|
||||
Ok(Self { listening_address })
|
||||
}
|
||||
|
||||
pub fn get_listening_address(&self) -> SocketAddr {
|
||||
self.listening_address
|
||||
}
|
||||
}
|
||||
91
remote_signer/client/src/rest_api.rs
Normal file
91
remote_signer/client/src/rest_api.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use crate::config::Config;
|
||||
use client_backend::{Backend, Storage};
|
||||
use futures::future::TryFutureExt;
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Server};
|
||||
use slog::{info, warn};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use task_executor::TaskExecutor;
|
||||
use types::{ChainSpec, EthSpec};
|
||||
|
||||
pub struct Context<E: EthSpec, S: Send + Sync> {
|
||||
pub config: Config,
|
||||
pub executor: TaskExecutor,
|
||||
pub log: slog::Logger,
|
||||
pub backend: Backend<S>,
|
||||
pub eth_spec_instance: E,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
pub fn start_server<E: EthSpec, S: Storage>(
|
||||
executor: TaskExecutor,
|
||||
config: Config,
|
||||
backend: Backend<S>,
|
||||
eth_spec_instance: E,
|
||||
) -> Result<SocketAddr, hyper::Error> {
|
||||
let log = executor.log();
|
||||
|
||||
let context = Arc::new(Context {
|
||||
executor: executor.clone(),
|
||||
log: log.clone(),
|
||||
config: config.clone(),
|
||||
backend,
|
||||
eth_spec_instance,
|
||||
spec: E::default_spec(),
|
||||
});
|
||||
|
||||
// Define the function that will build the request handler.
|
||||
let make_service = make_service_fn(move |_socket: &AddrStream| {
|
||||
let ctx = context.clone();
|
||||
|
||||
async move {
|
||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||
crate::router::on_http_request(req, ctx.clone())
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let bind_addr = (config.listen_address, config.port).into();
|
||||
let server = Server::bind(&bind_addr).serve(make_service);
|
||||
|
||||
// Determine the address the server is actually listening on.
|
||||
//
|
||||
// This may be different to `bind_addr` if bind port was 0 (this allows the OS to choose a free
|
||||
// port).
|
||||
let actual_listen_addr = server.local_addr();
|
||||
|
||||
// Build a channel to kill the HTTP server.
|
||||
let exit = executor.exit();
|
||||
let inner_log = log.clone();
|
||||
let server_exit = async move {
|
||||
let _ = exit.await;
|
||||
info!(inner_log, "HTTP service shutdown");
|
||||
};
|
||||
|
||||
// Configure the `hyper` server to gracefully shutdown when the shutdown channel is triggered.
|
||||
let inner_log = log.clone();
|
||||
let server_future = server
|
||||
.with_graceful_shutdown(async {
|
||||
server_exit.await;
|
||||
})
|
||||
.map_err(move |e| {
|
||||
warn!(
|
||||
inner_log,
|
||||
"HTTP server failed to start, Unable to bind"; "address" => format!("{:?}", e)
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|_| ());
|
||||
|
||||
info!(
|
||||
log,
|
||||
"HTTP API started";
|
||||
"address" => format!("{}", actual_listen_addr.ip()),
|
||||
"port" => actual_listen_addr.port(),
|
||||
);
|
||||
|
||||
executor.spawn_without_exit(server_future, "http");
|
||||
|
||||
Ok(actual_listen_addr)
|
||||
}
|
||||
101
remote_signer/client/src/router.rs
Normal file
101
remote_signer/client/src/router.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use crate::api_error::ApiError;
|
||||
use crate::backend::{get_keys, sign_message};
|
||||
use crate::handler::Handler;
|
||||
use crate::rest_api::Context;
|
||||
use crate::upcheck::upcheck;
|
||||
use client_backend::Storage;
|
||||
use hyper::{Body, Method, Request, Response};
|
||||
use slog::debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use types::EthSpec;
|
||||
|
||||
pub async fn on_http_request<E: EthSpec, S: Storage>(
|
||||
req: Request<Body>,
|
||||
ctx: Arc<Context<E, S>>,
|
||||
) -> Result<Response<Body>, ApiError> {
|
||||
let path = req.uri().path().to_string();
|
||||
let received_instant = Instant::now();
|
||||
let log = ctx.log.clone();
|
||||
|
||||
match route(req, ctx).await {
|
||||
Ok(response) => {
|
||||
debug!(
|
||||
log,
|
||||
"HTTP API request successful";
|
||||
"path" => path,
|
||||
"duration_ms" => Instant::now().duration_since(received_instant).as_millis()
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
Err(error) => {
|
||||
debug!(
|
||||
log,
|
||||
"HTTP API request failure";
|
||||
"path" => path,
|
||||
"duration_ms" => Instant::now().duration_since(received_instant).as_millis()
|
||||
);
|
||||
Ok(error.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn route<E: EthSpec, S: Storage>(
|
||||
req: Request<Body>,
|
||||
ctx: Arc<Context<E, S>>,
|
||||
) -> Result<Response<Body>, ApiError> {
|
||||
let path = req.uri().path().to_string();
|
||||
let method = req.method().clone();
|
||||
let ctx = ctx.clone();
|
||||
let handler = Handler::new(req, ctx)?;
|
||||
|
||||
match (method, path.as_ref()) {
|
||||
(Method::GET, "/upcheck") => handler.static_value(upcheck()).await?.serde_encodings(),
|
||||
|
||||
(Method::GET, "/keys") => handler.in_blocking_task(get_keys).await?.serde_encodings(),
|
||||
|
||||
(Method::POST, _) => route_post(&path, handler).await,
|
||||
|
||||
_ => Err(ApiError::NotFound(
|
||||
"Request path and/or method not found.".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Responds to all the POST requests.
|
||||
///
|
||||
/// Should be deprecated once a better routing library is used, such as `warp`
|
||||
async fn route_post<E: EthSpec, S: Storage>(
|
||||
path: &str,
|
||||
handler: Handler<E, S>,
|
||||
) -> Result<Response<Body>, ApiError> {
|
||||
let mut path_segments = path[1..].trim_end_matches('/').split('/');
|
||||
|
||||
match path_segments.next() {
|
||||
Some("sign") => {
|
||||
let path_segments_count = path_segments.clone().count();
|
||||
|
||||
if path_segments_count == 0 {
|
||||
return Err(ApiError::BadRequest(
|
||||
"Parameter public_key needed in route /sign/:public_key".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if path_segments_count > 1 {
|
||||
return Err(ApiError::BadRequest(
|
||||
"Only one path segment is allowed after /sign".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
handler
|
||||
.allow_body()
|
||||
.in_blocking_task(sign_message)
|
||||
.await?
|
||||
.serde_encodings()
|
||||
}
|
||||
_ => Err(ApiError::NotFound(
|
||||
"Request path and/or method not found.".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
78
remote_signer/client/src/signing_root.rs
Normal file
78
remote_signer/client/src/signing_root.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use crate::api_error::ApiError;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{from_value, Value};
|
||||
|
||||
use types::{
|
||||
AttestationData, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SignMessageRequestBody {
|
||||
/// BLS Signature domain.
|
||||
/// Supporting `beacon_proposer`, `beacon_attester`, and `randao`.
|
||||
/// As defined in
|
||||
/// * https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types
|
||||
/// * in lowercase, omitting the `domain` prefix.
|
||||
bls_domain: String,
|
||||
|
||||
/// Supporting `block`, `attestation`, and `epoch`.
|
||||
/// (In LH these are `BeaconBlock`, `AttestationData`, and `Epoch`).
|
||||
/// As defined in
|
||||
/// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/block.yaml
|
||||
/// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/attestation.yaml
|
||||
/// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml
|
||||
data: Value,
|
||||
|
||||
/// A `Fork` object containing previous and current versions.
|
||||
/// As defined in
|
||||
/// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml
|
||||
fork: Fork,
|
||||
|
||||
/// A `Hash256` for domain separation and chain versioning.
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_signing_root<E: EthSpec>(
|
||||
req: &hyper::Request<std::vec::Vec<u8>>,
|
||||
spec: ChainSpec,
|
||||
) -> Result<Hash256, ApiError> {
|
||||
let body: SignMessageRequestBody = serde_json::from_slice(req.body()).map_err(|e| {
|
||||
ApiError::BadRequest(format!("Unable to parse body message from JSON: {:?}", e))
|
||||
})?;
|
||||
|
||||
let get_domain = |epoch, bls_domain| {
|
||||
spec.get_domain(epoch, bls_domain, &body.fork, body.genesis_validators_root)
|
||||
};
|
||||
|
||||
match body.bls_domain.as_str() {
|
||||
"beacon_proposer" => {
|
||||
let block = from_value::<BeaconBlock<E>>(body.data.clone()).map_err(|e| {
|
||||
ApiError::BadRequest(format!("Unable to parse block from JSON: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(block.signing_root(get_domain(block.epoch(), Domain::BeaconProposer)))
|
||||
}
|
||||
|
||||
"beacon_attester" => {
|
||||
let attestation = from_value::<AttestationData>(body.data.clone()).map_err(|e| {
|
||||
ApiError::BadRequest(format!("Unable to parse attestation from JSON: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(attestation
|
||||
.signing_root(get_domain(attestation.target.epoch, Domain::BeaconAttester)))
|
||||
}
|
||||
|
||||
"randao" => {
|
||||
let epoch = from_value::<Epoch>(body.data.clone()).map_err(|e| {
|
||||
ApiError::BadRequest(format!("Unable to parse attestation from JSON: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(epoch.signing_root(get_domain(epoch, Domain::Randao)))
|
||||
}
|
||||
|
||||
s => Err(ApiError::BadRequest(format!(
|
||||
"Unsupported bls_domain parameter: {}",
|
||||
s
|
||||
))),
|
||||
}
|
||||
}
|
||||
7
remote_signer/client/src/upcheck.rs
Normal file
7
remote_signer/client/src/upcheck.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use crate::api_response::UpcheckApiResponse;
|
||||
|
||||
pub fn upcheck() -> UpcheckApiResponse {
|
||||
UpcheckApiResponse {
|
||||
status: "OK".to_string(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user