Cleanup HTTP APIs, update ports to avoid gap, rustfmt

Moved the HTTP APIs away from the REST endpoint abstraction and
to simpler Hyper handlers. Re-established all routes as v1.
Changed wallet receiver port to 13415 to avoid a gap in port
numbers.

Finally, rustfmt seems to have ignored specific files arguments,
running on everything.
This commit is contained in:
Ignotus Peverell
2017-10-31 19:32:33 -04:00
parent 05d22cb632
commit e4ebb7c7cb
78 changed files with 1705 additions and 1928 deletions
+24 -31
View File
@@ -34,37 +34,34 @@ fn refresh_output(out: &mut OutputData, api_out: &api::Output) {
match out.status {
OutputStatus::Unconfirmed => {
out.status = OutputStatus::Unspent;
},
}
_ => (),
}
}
// Transitions a local wallet output (based on it not being in the node utxo set) -
// Transitions a local wallet output (based on it not being in the node utxo
// set) -
// Unspent -> Spent
// Locked -> Spent
fn mark_spent_output(out: &mut OutputData) {
match out.status {
OutputStatus::Unspent | OutputStatus::Locked => {
out.status = OutputStatus::Spent
},
OutputStatus::Unspent | OutputStatus::Locked => out.status = OutputStatus::Spent,
_ => (),
}
}
/// Builds a single api query to retrieve the latest output data from the node.
/// So we can refresh the local wallet outputs.
pub fn refresh_outputs(
config: &WalletConfig,
keychain: &Keychain,
) -> Result<(), Error> {
pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(), Error> {
debug!(LOGGER, "Refreshing wallet outputs");
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
let mut commits: Vec<pedersen::Commitment> = vec![];
// build a local map of wallet outputs by commits
// and a list of outputs we wantot query the node for
// and a list of outputs we wantot query the node for
let _ = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
for out in wallet_data.outputs
for out in wallet_data
.outputs
.values()
.filter(|out| out.root_key_id == keychain.root_key_id())
.filter(|out| out.status != OutputStatus::Spent)
@@ -77,7 +74,7 @@ pub fn refresh_outputs(
});
// build the necessary query params -
// ?id=xxx&id=yyy&id=zzz
// ?id=xxx&id=yyy&id=zzz
let query_params: Vec<String> = commits
.iter()
.map(|commit| {
@@ -88,7 +85,7 @@ pub fn refresh_outputs(
let query_string = query_params.join("&");
let url = format!(
"{}/v2/chain/utxos?{}",
"{}/v1/chain/utxos?{}",
config.check_node_api_http_addr,
query_string,
);
@@ -96,32 +93,28 @@ pub fn refresh_outputs(
// build a map of api outputs by commit so we can look them up efficiently
let mut api_outputs: HashMap<pedersen::Commitment, api::Output> = HashMap::new();
match api::client::get::<Vec<api::Output>>(url.as_str()) {
Ok(outputs) => {
for out in outputs {
api_outputs.insert(out.commit, out);
}
Ok(outputs) => for out in outputs {
api_outputs.insert(out.commit, out);
},
Err(_) => {},
Err(_) => {}
};
// now for each commit, find the output in the wallet and
// the corresponding api output (if it exists)
// and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock.
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
for commit in commits {
let id = wallet_outputs.get(&commit).unwrap();
if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) {
match api_outputs.get(&commit) {
Some(api_output) => refresh_output(&mut output.get_mut(), api_output),
None => mark_spent_output(&mut output.get_mut()),
};
}
// the corresponding api output (if it exists)
// and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock.
WalletData::with_wallet(&config.data_file_dir, |wallet_data| for commit in commits {
let id = wallet_outputs.get(&commit).unwrap();
if let Entry::Occupied(mut output) = wallet_data.outputs.entry(id.to_hex()) {
match api_outputs.get(&commit) {
Some(api_output) => refresh_output(&mut output.get_mut(), api_output),
None => mark_spent_output(&mut output.get_mut()),
};
}
})
}
pub fn get_tip_from_node(config: &WalletConfig) -> Result<api::Tip, Error> {
let url = format!("{}/v2/chain", config.check_node_api_http_addr);
let url = format!("{}/v1/chain", config.check_node_api_http_addr);
api::client::get::<api::Tip>(url.as_str()).map_err(|e| Error::Node(e))
}
+10 -6
View File
@@ -33,7 +33,10 @@ pub fn create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Erro
retry_backoff_forever(|| {
let res = single_create_coinbase(&url, &block_fees);
if let Err(_) = res {
error!(LOGGER, "Failed to get coinbase via wallet API (will retry)...");
error!(
LOGGER,
"Failed to get coinbase via wallet API (will retry)..."
);
}
res
})
@@ -41,11 +44,12 @@ pub fn create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Erro
/// Runs the specified function wrapped in some basic retry logic.
fn retry_backoff_forever<F, R>(f: F) -> Result<R, Error>
where F: (FnMut() -> Result<R, Error>)
where
F: FnMut() -> Result<R, Error>,
{
let mut core = reactor::Core::new()?;
let retry_strategy = FibonacciBackoff::from_millis(100)
.max_delay(time::Duration::from_secs(10));
let retry_strategy =
FibonacciBackoff::from_millis(100).max_delay(time::Duration::from_secs(10));
let retry_future = Retry::spawn(core.handle(), retry_strategy, f);
let res = core.run(retry_future).unwrap();
Ok(res)
@@ -63,8 +67,8 @@ fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, E
let work = client.request(req).and_then(|res| {
res.body().concat2().and_then(move |body| {
let coinbase: CbData = serde_json::from_slice(&body)
.map_err(|e| {io::Error::new(io::ErrorKind::Other, e)})?;
let coinbase: CbData =
serde_json::from_slice(&body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(coinbase)
})
});
+9 -15
View File
@@ -33,13 +33,10 @@ pub struct CoinbaseHandler {
impl CoinbaseHandler {
fn build_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error> {
let (out, kern, block_fees) = receive_coinbase(
&self.config,
&self.keychain,
block_fees,
).map_err(|e| {
api::Error::Internal(format!("Error building coinbase: {:?}", e))
})?;
let (out, kern, block_fees) = receive_coinbase(&self.config, &self.keychain, block_fees)
.map_err(|e| {
api::Error::Internal(format!("Error building coinbase: {:?}", e))
})?;
let out_bin = ser::ser_vec(&out).map_err(|e| {
api::Error::Internal(format!("Error serializing output: {:?}", e))
@@ -50,13 +47,9 @@ impl CoinbaseHandler {
})?;
let key_id_bin = match block_fees.key_id {
Some(key_id) => {
ser::ser_vec(&key_id).map_err(|e| {
api::Error::Internal(
format!("Error serializing kernel: {:?}", e),
)
})?
}
Some(key_id) => ser::ser_vec(&key_id).map_err(|e| {
api::Error::Internal(format!("Error serializing kernel: {:?}", e))
})?,
None => vec![],
};
@@ -68,7 +61,8 @@ impl CoinbaseHandler {
}
}
// TODO - error handling - what to return if we fail to get the wallet lock for some reason...
// TODO - error handling - what to return if we fail to get the wallet lock for
// some reason...
impl Handler for CoinbaseHandler {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let struct_body = req.get::<bodyparser::Struct<BlockFees>>();
+5 -8
View File
@@ -22,17 +22,14 @@ pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
// just read the wallet here, no need for a write lock
let _ = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
// get the current height via the api
// if we cannot get the current height use the max height known to the wallet
// if we cannot get the current height use the max height known to the wallet
let current_height = match checker::get_tip_from_node(config) {
Ok(tip) => tip.height,
Err(_) => {
match wallet_data.outputs.values().map(|out| out.height).max() {
Some(height) => height,
None => 0,
}
}
Err(_) => match wallet_data.outputs.values().map(|out| out.height).max() {
Some(height) => height,
None => 0,
},
};
println!("Outputs - ");
+7 -7
View File
@@ -14,24 +14,24 @@
//! Library module for the main wallet functionalities provided by Grin.
extern crate byteorder;
extern crate blake2_rfc as blake2;
#[macro_use]
extern crate slog;
extern crate byteorder;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate slog;
extern crate bodyparser;
extern crate futures;
extern crate tokio_core;
extern crate tokio_retry;
extern crate hyper;
extern crate iron;
#[macro_use]
extern crate router;
extern crate tokio_core;
extern crate tokio_retry;
extern crate grin_api as api;
extern crate grin_core as core;
@@ -48,6 +48,6 @@ pub mod client;
pub mod server;
pub use info::show_info;
pub use receiver::{WalletReceiver, receive_json_tx};
pub use sender::{issue_send_tx, issue_burn_tx};
pub use receiver::{receive_json_tx, WalletReceiver};
pub use sender::{issue_burn_tx, issue_send_tx};
pub use types::{BlockFees, CbData, Error, WalletConfig, WalletReceiveRequest, WalletSeed};
+42 -88
View File
@@ -15,45 +15,18 @@
//! Provides the JSON/HTTP API for wallets to receive payments. Because
//! receiving money in MimbleWimble requires an interactive exchange, a
//! wallet server that's running at all time is required in many cases.
//!
//! The API looks like this:
//!
//! POST /v1/wallet/receive
//! > {
//! > "amount": 10,
//! > "blind_sum": "a12b7f...",
//! > "tx": "f083de...",
//! > }
//!
//! < {
//! < "tx": "f083de...",
//! < "status": "ok"
//! < }
//!
//! POST /v1/wallet/finalize
//! > {
//! > "tx": "f083de...",
//! > }
//!
//! POST /v1/wallet/receive_coinbase
//! > {
//! > "amount": 1,
//! > }
//!
//! < {
//! < "output": "8a90bc...",
//! < "kernel": "f083de...",
//! < }
//!
//! Note that while at this point the finalize call is completely unecessary, a
//! double-exchange will be required as soon as we support Schnorr signatures.
//! So we may as well have it in place already.
use std::io::Read;
use core::consensus::reward;
use core::core::{Block, Transaction, TxKernel, Output, build};
use core::core::{build, Block, Output, Transaction, TxKernel};
use core::ser;
use api::{self, ApiEndpoint, Operation, ApiResult};
use api;
use iron::prelude::*;
use iron::Handler;
use iron::status;
use keychain::{BlindingFactor, Identifier, Keychain};
use serde_json;
use types::*;
use util;
use util::LOGGER;
@@ -77,8 +50,8 @@ pub fn receive_json_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))?;
let _: () =
api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).map_err(|e| Error::Node(e))?;
Ok(())
}
@@ -90,46 +63,27 @@ pub struct WalletReceiver {
pub config: WalletConfig,
}
impl ApiEndpoint for WalletReceiver {
type ID = String;
type T = String;
type OP_IN = WalletReceiveRequest;
type OP_OUT = CbData;
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))?;
fn operations(&self) -> Vec<Operation> {
vec![Operation::Custom("receive_json_tx".to_string())]
}
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();
fn operation(&self, op: String, input: WalletReceiveRequest) -> ApiResult<CbData> {
match op.as_str() {
"receive_json_tx" => {
match input {
WalletReceiveRequest::PartialTransaction(partial_tx_str) => {
debug!(
LOGGER,
"Operation {} with transaction {}",
op,
&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();
// TODO: Return emptiness for now, should be a proper enum return type
Ok(CbData {
output: String::from(""),
kernel: String::from(""),
key_id: String::from(""),
})
}
_ => Err(api::Error::Argument(format!("Incorrect request data: {}", op))),
}
Ok(Response::with(status::Ok))
}
_ => Err(api::Error::Argument(format!("Unknown operation: {}", op))),
_ => Ok(Response::with(
(status::BadRequest, format!("Incorrect request data.")),
)),
}
}
}
@@ -168,7 +122,7 @@ fn next_available_key(
pub fn receive_coinbase(
config: &WalletConfig,
keychain: &Keychain,
block_fees: &BlockFees
block_fees: &BlockFees,
) -> Result<(Output, TxKernel, BlockFees), Error> {
let root_key_id = keychain.root_key_id();
let key_id = block_fees.key_id();
@@ -208,11 +162,7 @@ pub fn receive_coinbase(
debug!(LOGGER, "block_fees updated - {:?}", block_fees);
let (out, kern) = Block::reward_output(
&keychain,
&key_id,
block_fees.fees,
)?;
let (out, kern) = Block::reward_output(&keychain, &key_id, block_fees.fees)?;
Ok((out, kern, block_fees))
}
@@ -229,8 +179,8 @@ fn receive_transaction(
let (key_id, derivation) = next_available_key(config, keychain)?;
// double check the fee amount included in the partial tx
// we don't necessarily want to just trust the sender
// we could just overwrite the fee here (but we won't) due to the ecdsa sig
// we don't necessarily want to just trust the sender
// we could just overwrite the fee here (but we won't) due to the ecdsa sig
let fee = tx_fee(partial.inputs.len(), partial.outputs.len() + 1, None);
if fee != partial.fee {
return Err(Error::FeeDispute {
@@ -241,14 +191,18 @@ fn receive_transaction(
let out_amount = amount - fee;
let (tx_final, _) = build::transaction(vec![
build::initial_tx(partial),
build::with_excess(blinding),
build::output(out_amount, key_id.clone()),
let (tx_final, _) = build::transaction(
vec![
build::initial_tx(partial),
build::with_excess(blinding),
build::output(out_amount, key_id.clone()),
// build::with_fee(fee_amount),
], keychain)?;
],
keychain,
)?;
// make sure the resulting transaction is valid (could have been lied to on excess).
// make sure the resulting transaction is valid (could have been lied to on
// excess).
tx_final.validate(&keychain.secp())?;
// operate within a lock on wallet data
+14 -13
View File
@@ -14,9 +14,9 @@
use api;
use checker;
use core::core::{Transaction, build};
use core::core::{build, Transaction};
use core::ser;
use keychain::{BlindingFactor, Keychain, Identifier};
use keychain::{BlindingFactor, Identifier, Keychain};
use receiver::TxWrapper;
use types::*;
use util::LOGGER;
@@ -55,7 +55,7 @@ pub fn issue_send_tx(
if dest == "stdout" {
println!("{}", json_tx);
} else if &dest[..4] == "http" {
let url = format!("{}/v1/receive/receive_json_tx", &dest);
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!(
@@ -90,7 +90,7 @@ fn build_send_tx(
let mut parts = inputs_and_change(&coins, config, keychain, key_id, amount)?;
// This is more proof of concept than anything but here we set lock_height
// on tx being sent (based on current chain height via api).
// on tx being sent (based on current chain height via api).
parts.push(build::with_lock_height(lock_height));
let (tx, blind) = build::transaction(parts, &keychain)?;
@@ -130,8 +130,8 @@ pub fn issue_burn_tx(
let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).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))?;
let _: () =
api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).map_err(|e| Error::Node(e))?;
Ok(())
}
@@ -165,15 +165,15 @@ fn inputs_and_change(
}
// sender is responsible for setting the fee on the partial tx
// recipient should double check the fee calculation and not blindly trust the
// sender
// recipient should double check the fee calculation and not blindly trust the
// sender
let fee = tx_fee(coins.len(), 2, None);
parts.push(build::with_fee(fee));
// if we are spending 10,000 coins to send 1,000 then our change will be 9,000
// the fee will come out of the amount itself
// if the fee is 80 then the recipient will only receive 920
// but our change will still be 9,000
// the fee will come out of the amount itself
// if the fee is 80 then the recipient will only receive 920
// but our change will still be 9,000
let change = total - amount;
// build inputs using the appropriate derived key_ids
@@ -200,7 +200,8 @@ fn inputs_and_change(
is_coinbase: false,
});
// now lock the ouputs we're spending so we avoid accidental double spend attempt
// now lock the ouputs we're spending so we avoid accidental double spend
// attempt
for coin in coins {
wallet_data.lock_output(coin);
}
@@ -216,7 +217,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();
+8 -14
View File
@@ -27,28 +27,22 @@ pub fn start_rest_apis(wallet_config: WalletConfig, keychain: Keychain) {
wallet_config.api_http_addr
);
let mut apis = ApiServer::new("/v1".to_string());
apis.register_endpoint(
"/receive".to_string(),
WalletReceiver {
config: wallet_config.clone(),
keychain: keychain.clone(),
},
);
let receive_tx_handler = WalletReceiver {
config: wallet_config.clone(),
keychain: keychain.clone(),
};
let coinbase_handler = CoinbaseHandler {
config: wallet_config.clone(),
keychain: keychain.clone(),
};
// let tx_handler = TxHandler{};
let router = router!(
receive_tx: get "/receive/transaction" => receive_tx_handler,
receive_coinbase: post "/receive/coinbase" => coinbase_handler,
// receive_tx: post "/receive/tx" => tx_handler,
);
apis.register_handler("/v2", router);
);
let mut apis = ApiServer::new("/v1".to_string());
apis.register_handler(router);
apis.start(wallet_config.api_http_addr).unwrap_or_else(|e| {
error!(LOGGER, "Failed to start Grin wallet receiver: {}.", e);
});
+53 -60
View File
@@ -14,7 +14,7 @@
use blake2;
use rand::{thread_rng, Rng};
use std::{fmt, num, error};
use std::{error, fmt, num};
use std::convert::From;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Read, Write};
@@ -32,7 +32,7 @@ use tokio_retry::strategy::FibonacciBackoff;
use api;
use core::core::{Transaction, transaction};
use core::core::{transaction, Transaction};
use core::ser;
use keychain;
use util;
@@ -62,7 +62,7 @@ pub fn tx_fee(input_len: usize, output_len: usize, base_fee: Option<u64>) -> u64
#[derive(Debug)]
pub enum Error {
NotEnoughFunds(u64),
FeeDispute{sender_fee: u64, recipient_fee: u64},
FeeDispute { sender_fee: u64, recipient_fee: u64 },
Keychain(keychain::Error),
Transaction(transaction::Error),
Secp(secp::Error),
@@ -166,7 +166,7 @@ impl Default for WalletConfig {
fn default() -> WalletConfig {
WalletConfig {
enable_wallet: false,
api_http_addr: "0.0.0.0:13416".to_string(),
api_http_addr: "0.0.0.0:13415".to_string(),
check_node_api_http_addr: "http://127.0.0.1:13413".to_string(),
data_file_dir: ".".to_string(),
}
@@ -226,8 +226,10 @@ impl OutputData {
}
/// How many confirmations has this output received?
/// If height == 0 then we are either Unconfirmed or the output was cut-through
/// so we do not actually know how many confirmations this output had (and never will).
/// If height == 0 then we are either Unconfirmed or the output was
/// cut-through
/// so we do not actually know how many confirmations this output had (and
/// never will).
pub fn num_confirmations(&self, current_height: u64) -> u64 {
if self.status == OutputStatus::Unconfirmed {
0
@@ -239,21 +241,16 @@ impl OutputData {
}
/// Check if output is eligible for spending based on state and height.
pub fn eligible_to_spend(
&self,
current_height: u64,
minimum_confirmations: u64
) -> bool {
if [
OutputStatus::Spent,
OutputStatus::Locked,
].contains(&self.status) {
pub fn eligible_to_spend(&self, current_height: u64, minimum_confirmations: u64) -> bool {
if [OutputStatus::Spent, OutputStatus::Locked].contains(&self.status) {
return false;
} else if self.status == OutputStatus::Unconfirmed && self.is_coinbase {
return false;
} else if self.lock_height > current_height {
return false;
} else if self.status == OutputStatus::Unspent && self.height + minimum_confirmations <= current_height {
} else if self.status == OutputStatus::Unspent
&& self.height + minimum_confirmations <= current_height
{
return true;
} else if self.status == OutputStatus::Unconfirmed && minimum_confirmations == 0 {
return true;
@@ -306,11 +303,7 @@ impl WalletSeed {
SEED_FILE,
);
debug!(
LOGGER,
"Generating wallet seed file at: {}",
seed_file_path,
);
debug!(LOGGER, "Generating wallet seed file at: {}", seed_file_path,);
if Path::new(seed_file_path).exists() {
panic!("wallet seed file already exists");
@@ -333,11 +326,7 @@ impl WalletSeed {
SEED_FILE,
);
debug!(
LOGGER,
"Using wallet seed file at: {}",
seed_file_path,
);
debug!(LOGGER, "Using wallet seed file at: {}", seed_file_path,);
if Path::new(seed_file_path).exists() {
let mut file = File::open(seed_file_path)?;
@@ -369,10 +358,11 @@ pub struct WalletData {
}
impl WalletData {
/// Allows for reading wallet data (without needing to acquire the write lock).
/// Allows for reading wallet data (without needing to acquire the write
/// lock).
pub fn read_wallet<T, F>(data_file_dir: &str, f: F) -> Result<T, Error>
where F: FnOnce(&WalletData) -> T
where
F: FnOnce(&WalletData) -> T,
{
// open the wallet readonly and do what needs to be done with it
let data_file_path = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, DAT_FILE);
@@ -388,7 +378,8 @@ impl WalletData {
/// across operating systems, this just creates a lock file with a "should
/// not exist" option.
pub fn with_wallet<T, F>(data_file_dir: &str, f: F) -> Result<T, Error>
where F: FnOnce(&mut WalletData) -> T
where
F: FnOnce(&mut WalletData) -> T,
{
// create directory if it doesn't exist
fs::create_dir_all(data_file_dir).unwrap_or_else(|why| {
@@ -415,7 +406,7 @@ impl WalletData {
let retry_result = core.run(retry_future);
match retry_result {
Ok(_) => {},
Ok(_) => {}
Err(_) => {
error!(
LOGGER,
@@ -448,31 +439,33 @@ impl WalletData {
WalletData::read(data_file_path)
} else {
// just create a new instance, it will get written afterward
Ok(WalletData { outputs: HashMap::new() })
Ok(WalletData {
outputs: HashMap::new(),
})
}
}
/// Read the wallet data from disk.
fn read(data_file_path: &str) -> Result<WalletData, Error> {
let data_file =
File::open(data_file_path)
.map_err(|e| Error::WalletData(format!("Could not open {}: {}", data_file_path, e)))?;
serde_json::from_reader(data_file)
.map_err(|e| Error::WalletData(format!("Error reading {}: {}", data_file_path, e)))
let data_file = File::open(data_file_path).map_err(|e| {
Error::WalletData(format!("Could not open {}: {}", data_file_path, e))
})?;
serde_json::from_reader(data_file).map_err(|e| {
Error::WalletData(format!("Error reading {}: {}", data_file_path, e))
})
}
/// Write the wallet data to disk.
fn write(&self, data_file_path: &str) -> Result<(), Error> {
let mut data_file =
File::create(data_file_path)
.map_err(|e| {
Error::WalletData(format!("Could not create {}: {}", data_file_path, e))
})?;
let res_json = serde_json::to_vec_pretty(self)
.map_err(|e| Error::WalletData(format!("Error serializing wallet data: {}", e)))?;
data_file
.write_all(res_json.as_slice())
.map_err(|e| Error::WalletData(format!("Error writing {}: {}", data_file_path, e)))
let mut data_file = File::create(data_file_path).map_err(|e| {
Error::WalletData(format!("Could not create {}: {}", data_file_path, e))
})?;
let res_json = serde_json::to_vec_pretty(self).map_err(|e| {
Error::WalletData(format!("Error serializing wallet data: {}", e))
})?;
data_file.write_all(res_json.as_slice()).map_err(|e| {
Error::WalletData(format!("Error writing {}: {}", data_file_path, e))
})
}
/// Append a new output data to the wallet data.
@@ -503,7 +496,6 @@ impl WalletData {
current_height: u64,
minimum_confirmations: u64,
) -> Vec<OutputData> {
self.outputs
.values()
.filter(|out| {
@@ -537,10 +529,11 @@ struct JSONPartialTx {
/// Encodes the information for a partial transaction (not yet completed by the
/// receiver) into JSON.
pub fn partial_tx_to_json(receive_amount: u64,
blind_sum: keychain::BlindingFactor,
tx: Transaction)
-> String {
pub fn partial_tx_to_json(
receive_amount: u64,
blind_sum: keychain::BlindingFactor,
tx: Transaction,
) -> String {
let partial_tx = JSONPartialTx {
amount: receive_amount,
blind_sum: util::to_hex(blind_sum.secret_key().as_ref().to_vec()),
@@ -551,22 +544,22 @@ pub fn partial_tx_to_json(receive_amount: u64,
/// Reads a partial transaction encoded as JSON into the amount, sum of blinding
/// factors and the transaction itself.
pub fn partial_tx_from_json(keychain: &keychain::Keychain,
json_str: &str)
-> Result<(u64, keychain::BlindingFactor, Transaction), Error> {
pub fn partial_tx_from_json(
keychain: &keychain::Keychain,
json_str: &str,
) -> Result<(u64, keychain::BlindingFactor, Transaction), Error> {
let partial_tx: JSONPartialTx = serde_json::from_str(json_str)?;
let blind_bin = util::from_hex(partial_tx.blind_sum)?;
// TODO - turn some data into a blinding factor here somehow
// let blinding = SecretKey::from_slice(&secp, &blind_bin[..])?;
// 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 = ser::deserialize(&mut &tx_bin[..])
.map_err(|_| {
Error::Format("Could not deserialize transaction, invalid format.".to_string())
})?;
let tx = ser::deserialize(&mut &tx_bin[..]).map_err(|_| {
Error::Format("Could not deserialize transaction, invalid format.".to_string())
})?;
Ok((partial_tx.amount, blinding, tx))
}