fix the wallet receiver api (#222)

This commit is contained in:
AntiochP 2017-11-01 14:32:34 -04:00 committed by GitHub
parent 7a803a8dc1
commit 9e36b820f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 62 deletions

View file

@ -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<OUT, Error>
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

View file

@ -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);
},

View file

@ -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<CbData, Error> {
let mut core = reactor::Core::new()?;

View file

@ -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};

View file

@ -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<Response> {
let receive: WalletReceiveRequest = serde_json::from_reader(req.body.by_ref())
.map_err(|e| IronError::new(e, status::BadRequest))?;
let struct_body = req.get::<bodyparser::Struct<JSONPartialTx>>();
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, "")))
}
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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())
})?;