diff --git a/api/src/client.rs b/api/src/client.rs index 5a5838be5..a1b283ce2 100644 --- a/api/src/client.rs +++ b/api/src/client.rs @@ -40,19 +40,16 @@ where /// object as body on a given URL that returns a JSON object. Handles request /// building, JSON serialization and deserialization, and response code /// checking. -pub fn post<'a, IN, OUT>(url: &'a str, input: &IN) -> Result +pub fn post<'a, IN>(url: &'a str, input: &IN) -> Result<(), Error> where IN: Serialize, - for<'de> OUT: Deserialize<'de>, { let in_json = serde_json::to_string(input).map_err(|e| { Error::Internal(format!("Could not serialize data to JSON: {}", e)) })?; let client = hyper::Client::new(); - let res = check_error(client.post(url).body(&mut in_json.as_bytes()).send())?; - serde_json::from_reader(res).map_err(|e| { - Error::Internal(format!("Server returned invalid JSON: {}", e)) - }) + let _res = check_error(client.post(url).body(&mut in_json.as_bytes()).send())?; + Ok(()) } // convert hyper error and check for non success response codes diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 54aea075e..ea684a900 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -372,7 +372,7 @@ fn wallet_command(wallet_args: &ArgMatches) { let mut contents = String::new(); file.read_to_string(&mut contents) .expect("Unable to read transaction file."); - wallet::receive_json_tx(&wallet_config, &keychain, contents.as_str()).unwrap(); + wallet::receive_json_tx_str(&wallet_config, &keychain, contents.as_str()).unwrap(); } else { wallet::server::start_rest_apis(wallet_config, keychain); }, diff --git a/wallet/src/client.rs b/wallet/src/client.rs index d32e25f18..55045a1c0 100644 --- a/wallet/src/client.rs +++ b/wallet/src/client.rs @@ -55,6 +55,24 @@ where Ok(res) } +pub fn send_partial_tx(url: &str, partial_tx: &JSONPartialTx) -> Result<(), Error> { + single_send_partial_tx(url, partial_tx) +} + +fn single_send_partial_tx(url: &str, partial_tx: &JSONPartialTx) -> Result<(), Error> { + let mut core = reactor::Core::new()?; + let client = hyper::Client::new(&core.handle()); + + let mut req = Request::new(Method::Post, url.parse()?); + req.headers_mut().set(ContentType::json()); + let json = serde_json::to_string(&partial_tx)?; + req.set_body(json); + + let work = client.request(req); + let _ = core.run(work)?; + Ok(()) +} + /// Makes a single request to the wallet API to create a new coinbase output. fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result { let mut core = reactor::Core::new()?; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 05f74f4ce..8323c138c 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -48,6 +48,6 @@ pub mod client; pub mod server; pub use info::show_info; -pub use receiver::{receive_json_tx, WalletReceiver}; +pub use receiver::{receive_json_tx, receive_json_tx_str, WalletReceiver}; pub use sender::{issue_burn_tx, issue_send_tx}; pub use types::{BlockFees, CbData, Error, WalletConfig, WalletReceiveRequest, WalletSeed}; diff --git a/wallet/src/receiver.rs b/wallet/src/receiver.rs index 9600c99da..5428466d1 100644 --- a/wallet/src/receiver.rs +++ b/wallet/src/receiver.rs @@ -16,17 +16,17 @@ //! receiving money in MimbleWimble requires an interactive exchange, a //! wallet server that's running at all time is required in many cases. -use std::io::Read; - -use core::consensus::reward; -use core::core::{build, Block, Output, Transaction, TxKernel}; -use core::ser; -use api; +use bodyparser; use iron::prelude::*; use iron::Handler; use iron::status; -use keychain::{BlindingFactor, Identifier, Keychain}; use serde_json; + +use api; +use core::consensus::reward; +use core::core::{build, Block, Output, Transaction, TxKernel}; +use core::ser; +use keychain::{BlindingFactor, Identifier, Keychain}; use types::*; use util; use util::LOGGER; @@ -37,21 +37,30 @@ pub struct TxWrapper { pub tx_hex: String, } +pub fn receive_json_tx_str( + config: &WalletConfig, + keychain: &Keychain, + json_tx: &str, +) -> Result<(), Error> { + let partial_tx = serde_json::from_str(json_tx).unwrap(); + receive_json_tx(config, keychain, &partial_tx) +} + /// Receive an already well formed JSON transaction issuance and finalize the /// transaction, adding our receiving output, to broadcast to the rest of the /// network. pub fn receive_json_tx( config: &WalletConfig, keychain: &Keychain, - partial_tx_str: &str, + partial_tx: &JSONPartialTx, ) -> Result<(), Error> { - let (amount, blinding, partial_tx) = partial_tx_from_json(keychain, partial_tx_str)?; - let final_tx = receive_transaction(config, keychain, amount, blinding, partial_tx)?; + let (amount, blinding, tx) = read_partial_tx(keychain, partial_tx)?; + let final_tx = receive_transaction(config, keychain, amount, blinding, tx)?; let tx_hex = util::to_hex(ser::ser_vec(&final_tx).unwrap()); let url = format!("{}/v1/pool/push", config.check_node_api_http_addr.as_str()); - let _: () = - api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).map_err(|e| Error::Node(e))?; + api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }) + .map_err(|e| Error::Node(e))?; Ok(()) } @@ -65,25 +74,18 @@ pub struct WalletReceiver { impl Handler for WalletReceiver { fn handle(&self, req: &mut Request) -> IronResult { - let receive: WalletReceiveRequest = serde_json::from_reader(req.body.by_ref()) - .map_err(|e| IronError::new(e, status::BadRequest))?; + let struct_body = req.get::>(); - match receive { - WalletReceiveRequest::PartialTransaction(partial_tx_str) => { - debug!(LOGGER, "Receive with transaction {}", &partial_tx_str,); - receive_json_tx(&self.config, &self.keychain, &partial_tx_str) - .map_err(|e| { - api::Error::Internal( - format!("Error processing partial transaction: {:?}", e), - ) - }) - .unwrap(); - - Ok(Response::with(status::Ok)) - } - _ => Ok(Response::with( - (status::BadRequest, format!("Incorrect request data.")), - )), + if let Ok(Some(partial_tx)) = struct_body { + receive_json_tx(&self.config, &self.keychain, &partial_tx) + .map_err(|e| { + api::Error::Internal( + format!("Error processing partial transaction: {:?}", e), + )}) + .unwrap(); + Ok(Response::with(status::Ok)) + } else { + Ok(Response::with((status::BadRequest, ""))) } } } diff --git a/wallet/src/sender.rs b/wallet/src/sender.rs index f73b1f6a8..33522a4f0 100644 --- a/wallet/src/sender.rs +++ b/wallet/src/sender.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use serde_json; + use api; +use client; use checker; use core::core::{build, Transaction}; use core::ser; @@ -50,18 +53,16 @@ pub fn issue_send_tx( minimum_confirmations, lock_height, )?; - let json_tx = partial_tx_to_json(amount, blind_sum, tx); + + let partial_tx = build_partial_tx(amount, blind_sum, tx); if dest == "stdout" { + let json_tx = serde_json::to_string_pretty(&partial_tx).unwrap(); println!("{}", json_tx); } else if &dest[..4] == "http" { let url = format!("{}/v1/receive/transaction", &dest); debug!(LOGGER, "Posting partial transaction to {}", url); - let request = WalletReceiveRequest::PartialTransaction(json_tx); - let _: CbData = api::client::post(url.as_str(), &request).expect(&format!( - "Wallet receiver at {} unreachable, could not send transaction. Is it running?", - url - )); + client::send_partial_tx(&url, &partial_tx)?; } else { panic!("dest not in expected format: {}", dest); } @@ -201,7 +202,7 @@ fn inputs_and_change( }); // now lock the ouputs we're spending so we avoid accidental double spend - // attempt + // attempt for coin in coins { wallet_data.lock_output(coin); } @@ -217,7 +218,7 @@ mod test { #[test] // demonstrate that input.commitment == referenced output.commitment - // based on the public key and amount begin spent + // based on the public key and amount begin spent fn output_commitment_equals_input_commitment_on_spend() { let keychain = Keychain::from_random_seed().unwrap(); let key_id1 = keychain.derive_key_id(1).unwrap(); diff --git a/wallet/src/server.rs b/wallet/src/server.rs index 7ff92ba23..5f77f9359 100644 --- a/wallet/src/server.rs +++ b/wallet/src/server.rs @@ -37,9 +37,9 @@ pub fn start_rest_apis(wallet_config: WalletConfig, keychain: Keychain) { }; let router = router!( - receive_tx: get "/receive/transaction" => receive_tx_handler, + receive_tx: post "/receive/transaction" => receive_tx_handler, receive_coinbase: post "/receive/coinbase" => coinbase_handler, - ); + ); let mut apis = ApiServer::new("/v1".to_string()); apis.register_handler(router); diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 57f5b1311..afa7f6d84 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -521,7 +521,7 @@ impl WalletData { /// Helper in serializing the information a receiver requires to build a /// transaction. #[derive(Serialize, Deserialize, Debug, Clone)] -struct JSONPartialTx { +pub struct JSONPartialTx { amount: u64, blind_sum: String, tx: String, @@ -529,34 +529,31 @@ struct JSONPartialTx { /// Encodes the information for a partial transaction (not yet completed by the /// receiver) into JSON. -pub fn partial_tx_to_json( +pub fn build_partial_tx( receive_amount: u64, blind_sum: keychain::BlindingFactor, tx: Transaction, -) -> String { - let partial_tx = JSONPartialTx { +) -> JSONPartialTx { + JSONPartialTx { amount: receive_amount, blind_sum: util::to_hex(blind_sum.secret_key().as_ref().to_vec()), tx: util::to_hex(ser::ser_vec(&tx).unwrap()), - }; - serde_json::to_string_pretty(&partial_tx).unwrap() + } } -/// Reads a partial transaction encoded as JSON into the amount, sum of blinding +/// Reads a partial transaction into the amount, sum of blinding /// factors and the transaction itself. -pub fn partial_tx_from_json( +pub fn read_partial_tx( keychain: &keychain::Keychain, - json_str: &str, + partial_tx: &JSONPartialTx, ) -> Result<(u64, keychain::BlindingFactor, Transaction), Error> { - let partial_tx: JSONPartialTx = serde_json::from_str(json_str)?; + // let partial_tx: JSONPartialTx = serde_json::from_str(json_str)?; - let blind_bin = util::from_hex(partial_tx.blind_sum)?; + let blind_bin = util::from_hex(partial_tx.blind_sum.clone())?; - // TODO - turn some data into a blinding factor here somehow - // let blinding = SecretKey::from_slice(&secp, &blind_bin[..])?; let blinding = keychain::BlindingFactor::from_slice(keychain.secp(), &blind_bin[..])?; - let tx_bin = util::from_hex(partial_tx.tx)?; + let tx_bin = util::from_hex(partial_tx.tx.clone())?; let tx = ser::deserialize(&mut &tx_bin[..]).map_err(|_| { Error::Format("Could not deserialize transaction, invalid format.".to_string()) })?;