Implement json-rpc functionality

This commit is contained in:
Tyera Eulberg 2018-08-10 17:05:23 -06:00 committed by Tyera Eulberg
parent 3ae867bdd6
commit fc1dbddd93
4 changed files with 204 additions and 0 deletions

View File

@ -53,6 +53,10 @@ path = "src/bin/keygen.rs"
name = "solana-wallet"
path = "src/bin/wallet.rs"
[[bin]]
name = "solana-json-rpc"
path = "src/bin/json-rpc.rs"
[badges]
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
@ -75,6 +79,8 @@ futures = "0.1.21"
generic-array = { version = "0.11.1", default-features = false, features = ["serde"] }
getopts = "0.2"
influx_db_client = "0.3.4"
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc" }
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc" }
itertools = "0.7.8"
libc = "0.2.1"
log = "0.4.2"

80
src/bin/json-rpc.rs Normal file
View File

@ -0,0 +1,80 @@
//! The `json-rpc` service launches an HTTP server to listen for
//! Solana rpc requests.
#[macro_use]
extern crate clap;
extern crate dirs;
extern crate jsonrpc_http_server;
extern crate solana;
use clap::{App, Arg};
use jsonrpc_http_server::jsonrpc_core::*;
use jsonrpc_http_server::*;
use solana::crdt::NodeInfo;
use solana::fullnode::Config;
use solana::rpc::*;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
fn main() {
let matches = App::new("json-rpc")
.version(crate_version!())
.arg(
Arg::with_name("leader")
.short("l")
.long("leader")
.value_name("PATH")
.takes_value(true)
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.required(true)
.help("/path/to/id.json"),
)
.get_matches();
let leader: NodeInfo;
if let Some(l) = matches.value_of("leader") {
leader = read_leader(l).node_info;
} else {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
leader = NodeInfo::new_leader(&server_addr);
};
let id_path: String = matches
.value_of("keypair")
.expect("Unable to parse keypair path")
.to_string();
let mut io = MetaIoHandler::default();
let rpc = RpcSolImpl;
io.extend_with(rpc.to_delegate());
let rpc_addr = format!("0.0.0.0:{}", RPC_PORT);
let server = ServerBuilder::new(io)
.meta_extractor(move |_req: &hyper::Request| Meta {
leader: Some(leader.clone()),
keypair_location: Some(id_path.clone()),
})
.threads(4)
.cors(DomainsValidation::AllowOnly(vec![
AccessControlAllowOrigin::Any,
]))
.start_http(
&rpc_addr
.parse()
.expect("Unable to parse RPC server address"),
)
.expect("Unable to start RPC server");
server.wait();
}
fn read_leader(path: &str) -> Config {
let file = File::open(path).unwrap_or_else(|_| panic!("file not found: {}", path));
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}

View File

@ -41,6 +41,7 @@ pub mod request_processor;
pub mod request_stage;
pub mod result;
pub mod retransmit_stage;
pub mod rpc;
pub mod rpu;
pub mod service;
pub mod signature;
@ -63,6 +64,9 @@ extern crate byteorder;
extern crate chrono;
extern crate generic_array;
extern crate itertools;
#[macro_use]
extern crate jsonrpc_macros;
extern crate jsonrpc_http_server;
extern crate libc;
#[macro_use]
extern crate log;

114
src/rpc.rs Normal file
View File

@ -0,0 +1,114 @@
//! The `rpc` module implements the Solana rpc interface.
use bs58;
use client::mk_client;
use crdt::NodeInfo;
use jsonrpc_http_server::jsonrpc_core::*;
use signature::{read_keypair, KeypairUtil, Pubkey, Signature};
use std::mem;
pub const RPC_PORT: u16 = 8899;
#[derive(Clone)]
pub struct Meta {
pub leader: Option<NodeInfo>,
pub keypair_location: Option<String>,
}
impl Metadata for Meta {}
impl Default for Meta {
fn default() -> Self {
Meta {
leader: None,
keypair_location: None,
}
}
}
build_rpc_trait! {
pub trait RpcSol {
type Metadata;
#[rpc(meta, name = "solana_getAddress")]
fn address(&self, Self::Metadata) -> Result<String>;
#[rpc(meta, name = "solana_confirmTransaction")]
fn confirm_transaction(&self, Self::Metadata, String) -> Result<bool>;
#[rpc(meta, name = "solana_getBalance")]
fn get_balance(&self, Self::Metadata, String) -> Result<i64>;
#[rpc(meta, name = "solana_getTransactionCount")]
fn get_transaction_count(&self, Self::Metadata) -> Result<u64>;
#[rpc(meta, name = "solana_sendTransaction")]
fn send_transaction(&self, Self::Metadata, String, i64) -> Result<String>;
}
}
pub struct RpcSolImpl;
impl RpcSol for RpcSolImpl {
type Metadata = Meta;
fn address(&self, meta: Self::Metadata) -> Result<String> {
let client_keypair = read_keypair(&meta.keypair_location.unwrap());
Ok(bs58::encode(client_keypair.unwrap().pubkey()).into_string())
}
fn confirm_transaction(&self, meta: Self::Metadata, id: String) -> Result<bool> {
let signature_vec = bs58::decode(id)
.into_vec()
.expect("base58-encoded public key");
if signature_vec.len() != mem::size_of::<Signature>() {
Err(Error::invalid_request())
} else {
let signature = Signature::new(&signature_vec);
let mut client = mk_client(&meta.leader.unwrap());
let confirmation = client.check_signature(&signature);
Ok(confirmation)
}
}
fn get_balance(&self, meta: Self::Metadata, id: String) -> Result<i64> {
let pubkey_vec = bs58::decode(id)
.into_vec()
.expect("base58-encoded public key");
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
Err(Error::invalid_request())
} else {
let pubkey = Pubkey::new(&pubkey_vec);
let mut client = mk_client(&meta.leader.unwrap());
let balance = client.poll_get_balance(&pubkey);
match balance {
Ok(balance) => Ok(balance),
Err(_) => Err(Error::new(ErrorCode::ServerError(-32001))),
}
}
}
fn get_transaction_count(&self, meta: Self::Metadata) -> Result<u64> {
let mut client = mk_client(&meta.leader.unwrap());
let tx_count = client.transaction_count();
Ok(tx_count)
}
fn send_transaction(&self, meta: Self::Metadata, to: String, tokens: i64) -> Result<String> {
let client_keypair = read_keypair(&meta.keypair_location.unwrap()).unwrap();
let mut client = mk_client(&meta.leader.unwrap());
let last_id = client.get_last_id();
let to_pubkey_vec = bs58::decode(to)
.into_vec()
.expect("base58-encoded public key");
if to_pubkey_vec.len() != mem::size_of::<Pubkey>() {
Err(Error::invalid_request())
} else {
let to_pubkey = Pubkey::new(&to_pubkey_vec);
let signature = client
.transfer(tokens, &client_keypair, to_pubkey, &last_id)
.unwrap();
Ok(bs58::encode(signature).into_string())
}
}
}