//! Zebra supported RPC methods. //! //! Based on the [`zcashd` RPC methods](https://zcash.github.io/rpc/) //! as used by `lightwalletd.` //! //! Some parts of the `zcashd` RPC documentation are outdated. //! So this implementation follows the `lightwalletd` client implementation. use futures::FutureExt; use hex::FromHex; use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result}; use jsonrpc_derive::rpc; use tower::{buffer::Buffer, ServiceExt}; use zebra_chain::{ serialization::ZcashDeserialize, transaction::{self, Transaction}, }; use zebra_network::constants::USER_AGENT; use zebra_node_services::{mempool, BoxError}; #[cfg(test)] mod tests; #[rpc(server)] /// RPC method signatures. pub trait Rpc { /// getinfo /// /// Returns software information from the RPC server running Zebra. /// /// zcashd reference: /// /// Result: /// { /// "build": String, // Full application version /// "subversion", String, // Zebra user agent /// } /// /// Note 1: We only expose 2 fields as they are the only ones needed for /// lightwalletd: /// /// Note 2: is outdated so it does not /// show the fields we are exposing. However, this fields are part of the output /// as shown in the following zcashd code: /// /// Zcash open ticket to add this fields to the docs: #[rpc(name = "getinfo")] fn get_info(&self) -> Result; /// getblockchaininfo /// /// TODO: explain what the method does /// link to the zcashd RPC reference /// list the arguments and fields that lightwalletd uses /// note any other lightwalletd changes #[rpc(name = "getblockchaininfo")] fn get_blockchain_info(&self) -> Result; /// sendrawtransaction /// /// Sends the raw bytes of a signed transaction to the network, if the transaction is valid. /// /// zcashd reference: /// /// Result: a hexadecimal string of the hash of the sent transaction. /// /// Note: zcashd provides an extra `allowhighfees` parameter, but we don't yet because /// lightwalletd doesn't use it. #[rpc(name = "sendrawtransaction")] fn send_raw_transaction( &self, raw_transaction_hex: String, ) -> BoxFuture>; } /// RPC method implementations. pub struct RpcImpl where Mempool: tower::Service, { /// Zebra's application version. app_version: String, /// A handle to the mempool service. mempool: Buffer, } impl RpcImpl where Mempool: tower::Service, { /// Create a new instance of the RPC handler. pub fn new(app_version: String, mempool: Buffer) -> Self { RpcImpl { app_version, mempool, } } } impl Rpc for RpcImpl where Mempool: tower::Service + 'static, Mempool::Future: Send, { fn get_info(&self) -> Result { let response = GetInfo { build: self.app_version.clone(), subversion: USER_AGENT.into(), }; Ok(response) } fn get_blockchain_info(&self) -> Result { // TODO: dummy output data, fix in the context of #3143 let response = GetBlockChainInfo { chain: "TODO: main".to_string(), }; Ok(response) } fn send_raw_transaction( &self, raw_transaction_hex: String, ) -> BoxFuture> { let mempool = self.mempool.clone(); async move { let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex).map_err(|_| { Error::invalid_params("raw transaction is not specified as a hex string") })?; let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes) .map_err(|_| Error::invalid_params("raw transaction is structurally invalid"))?; let transaction_hash = raw_transaction.hash(); let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into()); let request = mempool::Request::Queue(vec![transaction_parameter]); let response = mempool.oneshot(request).await.map_err(|error| Error { code: ErrorCode::ServerError(0), message: error.to_string(), data: None, })?; let queue_results = match response { mempool::Response::Queued(results) => results, _ => unreachable!("incorrect response variant from mempool service"), }; assert_eq!( queue_results.len(), 1, "mempool service returned more results than expected" ); match &queue_results[0] { Ok(()) => Ok(SentTransactionHash(transaction_hash)), Err(error) => Err(Error { code: ErrorCode::ServerError(0), message: error.to_string(), data: None, }), } } .boxed() } } #[derive(serde::Serialize, serde::Deserialize)] /// Response to a `getinfo` RPC request. pub struct GetInfo { build: String, subversion: String, } #[derive(serde::Serialize, serde::Deserialize)] /// Response to a `getblockchaininfo` RPC request. pub struct GetBlockChainInfo { chain: String, // TODO: add other fields used by lightwalletd (#3143) } #[derive(Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] /// Response to a `sendrawtransaction` RPC request. /// /// A JSON string with the transaction hash in hexadecimal. pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);