Vote signing JSON RPC service (#1965)
* Vote signing JSON RPC service - barebone service that listens for RPC requests * Daemon for vote signer service * Add request APIs for JSON RPC * Cleanup of cargo dependencies * Fix compiler error
This commit is contained in:
parent
b00011a3f1
commit
549bfe7412
|
@ -1723,6 +1723,7 @@ dependencies = [
|
||||||
"solana-metrics 0.11.0",
|
"solana-metrics 0.11.0",
|
||||||
"solana-noop 0.11.0",
|
"solana-noop 0.11.0",
|
||||||
"solana-sdk 0.11.0",
|
"solana-sdk 0.11.0",
|
||||||
|
"solana-vote-signer 0.0.1",
|
||||||
"sys-info 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sys-info 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1904,6 +1905,20 @@ dependencies = [
|
||||||
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solana-vote-signer"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"solana-jsonrpc-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"solana-jsonrpc-http-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"solana-jsonrpc-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"solana-metrics 0.11.0",
|
||||||
|
"solana-sdk 0.11.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "solana-ws"
|
name = "solana-ws"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
|
@ -109,6 +109,7 @@ solana-lualoader = { path = "programs/native/lua_loader", version = "0.11.0" }
|
||||||
solana-metrics = { path = "metrics", version = "0.11.0" }
|
solana-metrics = { path = "metrics", version = "0.11.0" }
|
||||||
solana-noop = { path = "programs/native/noop", version = "0.11.0" }
|
solana-noop = { path = "programs/native/noop", version = "0.11.0" }
|
||||||
solana-sdk = { path = "sdk", version = "0.11.0" }
|
solana-sdk = { path = "sdk", version = "0.11.0" }
|
||||||
|
solana-vote-signer = { path = "vote-signer", version = "0.0.1" }
|
||||||
sys-info = "0.5.6"
|
sys-info = "0.5.6"
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
tokio-codec = "0.1"
|
tokio-codec = "0.1"
|
||||||
|
@ -147,4 +148,5 @@ members = [
|
||||||
"programs/native/erc20",
|
"programs/native/erc20",
|
||||||
"programs/native/lua_loader",
|
"programs/native/lua_loader",
|
||||||
"programs/native/noop",
|
"programs/native/noop",
|
||||||
|
"vote-signer",
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,6 +13,9 @@ pub enum RpcRequest {
|
||||||
GetTransactionCount,
|
GetTransactionCount,
|
||||||
RequestAirdrop,
|
RequestAirdrop,
|
||||||
SendTransaction,
|
SendTransaction,
|
||||||
|
RegisterNode,
|
||||||
|
SignVote,
|
||||||
|
DeregisterNode,
|
||||||
}
|
}
|
||||||
pub type RpcClient = reqwest::Client;
|
pub type RpcClient = reqwest::Client;
|
||||||
|
|
||||||
|
@ -53,6 +56,9 @@ impl RpcRequest {
|
||||||
RpcRequest::GetTransactionCount => "getTransactionCount",
|
RpcRequest::GetTransactionCount => "getTransactionCount",
|
||||||
RpcRequest::RequestAirdrop => "requestAirdrop",
|
RpcRequest::RequestAirdrop => "requestAirdrop",
|
||||||
RpcRequest::SendTransaction => "sendTransaction",
|
RpcRequest::SendTransaction => "sendTransaction",
|
||||||
|
RpcRequest::RegisterNode => "registerNode",
|
||||||
|
RpcRequest::SignVote => "signVote",
|
||||||
|
RpcRequest::DeregisterNode => "deregisterNode",
|
||||||
};
|
};
|
||||||
let mut request = json!({
|
let mut request = json!({
|
||||||
"jsonrpc": jsonrpc,
|
"jsonrpc": jsonrpc,
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "solana-vote-signer"
|
||||||
|
version = "0.0.1"
|
||||||
|
description = "Solana Vote Signing Service"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bs58 = "0.2.0"
|
||||||
|
clap = "2.31"
|
||||||
|
log = "0.4.2"
|
||||||
|
solana-sdk = { path = "../sdk", version = "0.11.0" }
|
||||||
|
solana-metrics = { path = "../metrics", version = "0.11.0" }
|
||||||
|
solana-jsonrpc-core = "0.3.0"
|
||||||
|
solana-jsonrpc-http-server = "0.3.0"
|
||||||
|
solana-jsonrpc-macros = "0.3.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "solana_vote_signer"
|
||||||
|
crate-type = ["lib"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "solana-vote-signer"
|
||||||
|
path = "src/bin/main.rs"
|
|
@ -0,0 +1,42 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
extern crate log;
|
||||||
|
extern crate solana_metrics;
|
||||||
|
extern crate solana_sdk;
|
||||||
|
extern crate solana_vote_signer;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use solana_vote_signer::rpc::VoteSignerRpcService;
|
||||||
|
use std::error;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
pub const RPC_PORT: u16 = 8989;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<error::Error>> {
|
||||||
|
solana_metrics::set_panic_hook("vote-signer");
|
||||||
|
|
||||||
|
let matches = App::new("vote-signer")
|
||||||
|
.version(crate_version!())
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("port")
|
||||||
|
.long("port")
|
||||||
|
.value_name("NUM")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("JSON RPC listener port"),
|
||||||
|
).get_matches();
|
||||||
|
|
||||||
|
let port = if let Some(p) = matches.value_of("port") {
|
||||||
|
p.to_string()
|
||||||
|
.parse()
|
||||||
|
.expect("Failed to parse JSON RPC Port")
|
||||||
|
} else {
|
||||||
|
RPC_PORT
|
||||||
|
};
|
||||||
|
|
||||||
|
let service =
|
||||||
|
VoteSignerRpcService::new(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port));
|
||||||
|
|
||||||
|
service.join().unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
pub mod rpc;
|
||||||
|
|
||||||
|
extern crate bs58;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
extern crate solana_jsonrpc_core as jsonrpc_core;
|
||||||
|
extern crate solana_jsonrpc_http_server as jsonrpc_http_server;
|
||||||
|
extern crate solana_sdk;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate solana_jsonrpc_macros as jsonrpc_macros;
|
|
@ -0,0 +1,147 @@
|
||||||
|
//! The `rpc` module implements the Vote signing service RPC interface.
|
||||||
|
|
||||||
|
use bs58;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use jsonrpc_http_server::*;
|
||||||
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
use solana_sdk::signature::Signature;
|
||||||
|
use std::mem;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread::{self, sleep, Builder, JoinHandle};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub struct VoteSignerRpcService {
|
||||||
|
thread_hdl: JoinHandle<()>,
|
||||||
|
exit: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoteSignerRpcService {
|
||||||
|
pub fn new(rpc_addr: SocketAddr) -> Self {
|
||||||
|
let request_processor = VoteSignRequestProcessor::default();
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let exit_ = exit.clone();
|
||||||
|
let thread_hdl = Builder::new()
|
||||||
|
.name("solana-vote-signer-jsonrpc".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let mut io = MetaIoHandler::default();
|
||||||
|
let rpc = VoteSignerRpcImpl;
|
||||||
|
io.extend_with(rpc.to_delegate());
|
||||||
|
|
||||||
|
let server =
|
||||||
|
ServerBuilder::with_meta_extractor(io, move |_req: &hyper::Request<hyper::Body>| Meta {
|
||||||
|
request_processor: request_processor.clone(),
|
||||||
|
rpc_addr,
|
||||||
|
}).threads(4)
|
||||||
|
.cors(DomainsValidation::AllowOnly(vec![
|
||||||
|
AccessControlAllowOrigin::Any,
|
||||||
|
]))
|
||||||
|
.start_http(&rpc_addr);
|
||||||
|
if server.is_err() {
|
||||||
|
warn!("JSON RPC service unavailable: unable to bind to RPC port {}. \nMake sure this port is not already in use by another application", rpc_addr.port());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while !exit_.load(Ordering::Relaxed) {
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
server.unwrap().close();
|
||||||
|
()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
VoteSignerRpcService { thread_hdl, exit }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit(&self) {
|
||||||
|
self.exit.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) -> thread::Result<()> {
|
||||||
|
self.exit();
|
||||||
|
self.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self) -> thread::Result<()> {
|
||||||
|
self.thread_hdl.join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Meta {
|
||||||
|
pub request_processor: VoteSignRequestProcessor,
|
||||||
|
pub rpc_addr: SocketAddr,
|
||||||
|
}
|
||||||
|
impl Metadata for Meta {}
|
||||||
|
|
||||||
|
build_rpc_trait! {
|
||||||
|
pub trait VoteSignerRpc {
|
||||||
|
type Metadata;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "registerNode")]
|
||||||
|
fn register(&self, Self::Metadata, String) -> Result<Pubkey>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "signVote")]
|
||||||
|
fn sign(&self, Self::Metadata, String) -> Result<Signature>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "deregisterNode")]
|
||||||
|
fn deregister(&self, Self::Metadata, String) -> Result<()>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VoteSignerRpcImpl;
|
||||||
|
impl VoteSignerRpc for VoteSignerRpcImpl {
|
||||||
|
type Metadata = Meta;
|
||||||
|
|
||||||
|
fn register(&self, meta: Self::Metadata, id: String) -> Result<Pubkey> {
|
||||||
|
info!("register rpc request received: {:?}", id);
|
||||||
|
let pubkey = get_pubkey(id)?;
|
||||||
|
meta.request_processor.register(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(&self, meta: Self::Metadata, id: String) -> Result<Signature> {
|
||||||
|
info!("sign rpc request received: {:?}", id);
|
||||||
|
let pubkey = get_pubkey(id)?;
|
||||||
|
meta.request_processor.sign(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deregister(&self, meta: Self::Metadata, id: String) -> Result<()> {
|
||||||
|
info!("deregister rpc request received: {:?}", id);
|
||||||
|
let pubkey = get_pubkey(id)?;
|
||||||
|
meta.request_processor.deregister(pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct VoteSignRequestProcessor {}
|
||||||
|
impl VoteSignRequestProcessor {
|
||||||
|
/// Process JSON-RPC request items sent via JSON-RPC.
|
||||||
|
pub fn register(&self, pubkey: Pubkey) -> Result<Pubkey> {
|
||||||
|
Ok(pubkey)
|
||||||
|
}
|
||||||
|
pub fn sign(&self, _pubkey: Pubkey) -> Result<Signature> {
|
||||||
|
let signature = [0u8; 16];
|
||||||
|
Ok(Signature::new(&signature))
|
||||||
|
}
|
||||||
|
pub fn deregister(&self, _pubkey: Pubkey) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pubkey(input: String) -> Result<Pubkey> {
|
||||||
|
let pubkey_vec = bs58::decode(input).into_vec().map_err(|err| {
|
||||||
|
info!("get_pubkey: invalid input: {:?}", err);
|
||||||
|
Error::invalid_request()
|
||||||
|
})?;
|
||||||
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
||||||
|
info!(
|
||||||
|
"get_pubkey: invalid pubkey_vec length: {}",
|
||||||
|
pubkey_vec.len()
|
||||||
|
);
|
||||||
|
Err(Error::invalid_request())
|
||||||
|
} else {
|
||||||
|
Ok(Pubkey::new(&pubkey_vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {}
|
Loading…
Reference in New Issue