diff --git a/doc/json-rpc.md b/doc/json-rpc.md new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/fullnode.rs b/src/fullnode.rs index b8fb60b6d22259..9349456e5dd8ae 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -3,6 +3,7 @@ use bank::Bank; use broadcast_stage::BroadcastStage; use crdt::{Crdt, NodeInfo, TestNode}; +use drone::DRONE_PORT; use entry::Entry; use ledger::read_ledger; use ncp::Ncp; @@ -202,8 +203,16 @@ impl Fullnode { ); thread_hdls.extend(rpu.thread_hdls()); + let mut drone_addr = node.data.contact_info.tpu; + drone_addr.set_port(DRONE_PORT); let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), RPC_PORT); - let rpc_service = JsonRpcService::new(bank.clone(), rpc_addr, exit.clone()); + let rpc_service = JsonRpcService::new( + &bank, + node.data.contact_info.tpu, + drone_addr, + rpc_addr, + exit.clone(), + ); thread_hdls.extend(rpc_service.thread_hdls()); let blob_recycler = BlobRecycler::default(); @@ -297,8 +306,16 @@ impl Fullnode { ); thread_hdls.extend(rpu.thread_hdls()); + let mut drone_addr = entry_point.contact_info.ncp; + drone_addr.set_port(DRONE_PORT); let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), RPC_PORT); - let rpc_service = JsonRpcService::new(bank.clone(), rpc_addr, exit.clone()); + let rpc_service = JsonRpcService::new( + &bank, + node.data.contact_info.tpu, + drone_addr, + rpc_addr, + exit.clone(), + ); thread_hdls.extend(rpc_service.thread_hdls()); let blob_recycler = BlobRecycler::default(); diff --git a/src/rpc.rs b/src/rpc.rs index b06a080f066e5e..5e45b6139211fe 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -1,16 +1,21 @@ //! The `rpc` module implements the Solana RPC interface. use bank::Bank; +use bincode::deserialize; use bs58; use jsonrpc_core::*; use jsonrpc_http_server::*; use service::Service; use signature::{Pubkey, Signature}; use std::mem; -use std::net::SocketAddr; +use std::net::{SocketAddr, UdpSocket}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::thread::{self, Builder, JoinHandle}; +use std::thread::{self, sleep, Builder, JoinHandle}; +use std::time::Duration; +use std::time::Instant; +use transaction::Transaction; +use wallet::request_airdrop; pub const RPC_PORT: u16 = 8899; @@ -19,8 +24,14 @@ pub struct JsonRpcService { } impl JsonRpcService { - pub fn new(bank: Arc, rpc_addr: SocketAddr, exit: Arc) -> Self { - let request_processor = JsonRpcRequestProcessor::new(bank); + pub fn new( + bank: &Arc, + transactions_addr: SocketAddr, + drone_addr: SocketAddr, + rpc_addr: SocketAddr, + exit: Arc, + ) -> Self { + let request_processor = JsonRpcRequestProcessor::new(bank.clone()); let thread_hdl = Builder::new() .name("solana-jsonrpc".to_string()) .spawn(move || { @@ -31,6 +42,8 @@ impl JsonRpcService { let server = ServerBuilder::with_meta_extractor(io, move |_req: &hyper::Request| Meta { request_processor: request_processor.clone(), + transactions_addr, + drone_addr, }).threads(4) .cors(DomainsValidation::AllowOnly(vec![ AccessControlAllowOrigin::Any, @@ -66,6 +79,8 @@ impl Service for JsonRpcService { #[derive(Clone)] pub struct Meta { pub request_processor: JsonRpcRequestProcessor, + pub transactions_addr: SocketAddr, + pub drone_addr: SocketAddr, } impl Metadata for Meta {} @@ -88,8 +103,11 @@ build_rpc_trait! { #[rpc(meta, name = "getTransactionCount")] fn get_transaction_count(&self, Self::Metadata) -> Result; - // #[rpc(meta, name = "sendTransaction")] - // fn send_transaction(&self, Self::Metadata, String, i64) -> Result; + #[rpc(meta, name= "requestAirdrop")] + fn request_airdrop(&self, Self::Metadata, String, u64) -> Result; + + #[rpc(meta, name = "sendTransaction")] + fn send_transaction(&self, Self::Metadata, Vec) -> Result; } } @@ -126,24 +144,42 @@ impl RpcSol for RpcSolImpl { fn get_transaction_count(&self, meta: Self::Metadata) -> Result { meta.request_processor.get_transaction_count() } - // fn send_transaction(&self, meta: Self::Metadata, to: String, tokens: i64) -> Result { - // 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::() { - // 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()) - // } - // } + fn request_airdrop(&self, meta: Self::Metadata, id: String, tokens: u64) -> Result { + let pubkey_vec = bs58::decode(id) + .into_vec() + .map_err(|_| Error::invalid_request())?; + if pubkey_vec.len() != mem::size_of::() { + return Err(Error::invalid_request()); + } + let pubkey = Pubkey::new(&pubkey_vec); + let previous_balance = meta + .request_processor + .get_balance(pubkey) + .map_err(|_| Error::internal_error())?; + request_airdrop(&meta.drone_addr, &pubkey, tokens).map_err(|_| Error::internal_error())?; + let now = Instant::now(); + let mut balance; + loop { + balance = meta + .request_processor + .get_balance(pubkey) + .map_err(|_| Error::internal_error())?; + if balance > previous_balance { + return Ok(true); + } else if now.elapsed().as_secs() > 5 { + return Err(Error::internal_error()); + } + sleep(Duration::from_millis(100)); + } + } + fn send_transaction(&self, meta: Self::Metadata, data: Vec) -> Result { + let tx: Transaction = deserialize(&data).map_err(|_| Error::invalid_request())?; + let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap(); + transactions_socket + .send_to(&data, &meta.transactions_addr) + .map_err(|_| Error::internal_error())?; + Ok(bs58::encode(tx.signature).into_string()) + } } #[derive(Clone)] pub struct JsonRpcRequestProcessor { @@ -182,6 +218,7 @@ mod tests { use jsonrpc_core::Response; use mint::Mint; use signature::{Keypair, KeypairUtil}; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; use transaction::Transaction; @@ -196,11 +233,17 @@ mod tests { bank.process_transaction(&tx).expect("process transaction"); let request_processor = JsonRpcRequestProcessor::new(Arc::new(bank)); + let transactions_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + let drone_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let mut io = MetaIoHandler::default(); let rpc = RpcSolImpl; io.extend_with(rpc.to_delegate()); - let meta = Meta { request_processor }; + let meta = Meta { + request_processor, + transactions_addr, + drone_addr, + }; let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#, @@ -236,6 +279,8 @@ mod tests { let req = r#"{"jsonrpc":"2.0","id":1,"method":"confirmTransaction","params":[1234567890]}"#; let meta = Meta { request_processor: JsonRpcRequestProcessor::new(Arc::new(bank)), + transactions_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), + drone_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), }; let res = io.handle_request_sync(req, meta); @@ -259,6 +304,8 @@ mod tests { r#"{"jsonrpc":"2.0","id":1,"method":"confirmTransaction","params":["a1b2c3d4e5"]}"#; let meta = Meta { request_processor: JsonRpcRequestProcessor::new(Arc::new(bank)), + transactions_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), + drone_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), }; let res = io.handle_request_sync(req, meta);