diff --git a/Cargo.toml b/Cargo.toml index 7ad657597..3d9c5f44b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ clap = "^2.23.3" daemonize = "^0.2.3" env_logger="^0.3.5" log = "^0.3" -serde = "~0.9.10" -serde_json = "~0.9.9" +serde = "~1.0.8" +serde_derive = "~1.0.8" +serde_json = "~1.0.2" tiny-keccak = "1.1" diff --git a/api/Cargo.toml b/api/Cargo.toml index 8978a7fc9..7df87e79b 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -5,11 +5,14 @@ authors = ["Ignotus Peverell "] workspace = ".." [dependencies] +grin_core = { path = "../core" } grin_chain = { path = "../chain" } +grin_util = { path = "../util" } +secp256k1zkp = { path = "../secp256k1zkp" } hyper = "~0.10.6" iron = "~0.5.1" log = "~0.3" router = "~0.5.1" -serde = "~0.9.10" -serde_json = "~0.9.8" +serde = "~1.0.8" +serde_json = "~1.0.2" diff --git a/api/src/client.rs b/api/src/client.rs index a07532110..fa8c0d7c2 100644 --- a/api/src/client.rs +++ b/api/src/client.rs @@ -26,8 +26,9 @@ use rest::Error; /// returns a JSON object. Handles request building, JSON deserialization and /// response code checking. pub fn get<'a, T>(url: &'a str) -> Result - where T: Deserialize + where for<'de> T: Deserialize<'de> { + println!("get {}", url); let client = hyper::Client::new(); let res = check_error(client.get(url).send())?; serde_json::from_reader(res) @@ -40,7 +41,7 @@ pub fn get<'a, T>(url: &'a str) -> Result /// checking. pub fn post<'a, IN, OUT>(url: &'a str, input: &IN) -> Result where IN: Serialize, - OUT: Deserialize + 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)))?; diff --git a/api/src/endpoints.rs b/api/src/endpoints.rs index 4c5d02ae9..787417efb 100644 --- a/api/src/endpoints.rs +++ b/api/src/endpoints.rs @@ -24,8 +24,12 @@ use std::sync::Arc; use std::thread; +use core::core::Output; +use core::core::hash::Hash; use chain::{self, Tip}; use rest::*; +use secp::pedersen::Commitment; +use util; /// ApiEndpoint implementation for the blockchain. Exposes the current chain /// state as a simple JSON object. @@ -50,13 +54,38 @@ impl ApiEndpoint for ChainApi { } } +/// ApiEndpoint implementation for outputs that have been included in the chain. +#[derive(Clone)] +pub struct OutputApi { + /// data store access + chain_store: Arc, +} + +impl ApiEndpoint for OutputApi { + type ID = String; + type T = Output; + type OP_IN = (); + type OP_OUT = (); + + fn operations(&self) -> Vec { + vec![Operation::Get] + } + + fn get(&self, id: String) -> ApiResult { + debug!("GET output {}", id); + let c = util::from_hex(id.clone()).map_err(|e| Error::Argument(format!("Not a valid commitment: {}", id)))?; + self.chain_store.get_output_by_commit(&Commitment::from_vec(c)).map_err(|e| Error::Internal(e.to_string())) + } +} + /// Start all server REST APIs. Just register all of them on a ApiServer /// instance and runs the corresponding HTTP server. pub fn start_rest_apis(addr: String, chain_store: Arc) { thread::spawn(move || { let mut apis = ApiServer::new("/v1".to_string()); - apis.register_endpoint("/chain".to_string(), ChainApi { chain_store: chain_store }); + apis.register_endpoint("/chain".to_string(), ChainApi { chain_store: chain_store.clone() }); + apis.register_endpoint("/chain/output".to_string(), OutputApi { chain_store: chain_store.clone() }); apis.start(&addr[..]).unwrap_or_else(|e| { error!("Failed to start API HTTP server: {}.", e); }); diff --git a/api/src/lib.rs b/api/src/lib.rs index 5b598415b..f6e8dce6a 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate grin_core as core; extern crate grin_chain as chain; +extern crate grin_util as util; +extern crate secp256k1zkp as secp; extern crate hyper; #[macro_use] diff --git a/api/src/rest.rs b/api/src/rest.rs index b274c0048..b7e5e9c01 100644 --- a/api/src/rest.rs +++ b/api/src/rest.rs @@ -31,6 +31,7 @@ use iron::modifiers::Header; use iron::middleware::Handler; use router::Router; use serde::{Serialize, Deserialize}; +use serde::de::DeserializeOwned; use serde_json; /// Errors that can be returned by an ApiEndpoint implementation. @@ -109,9 +110,9 @@ pub type ApiResult = ::std::result::Result; /// create and accepted by all other methods must have a string representation. pub trait ApiEndpoint: Clone + Send + Sync + 'static { type ID: ToString + FromStr; - type T: Serialize + Deserialize; - type OP_IN: Serialize + Deserialize; - type OP_OUT: Serialize + Deserialize; + type T: Serialize + DeserializeOwned; + type OP_IN: Serialize + DeserializeOwned; + type OP_OUT: Serialize + DeserializeOwned; fn operations(&self) -> Vec; @@ -251,7 +252,7 @@ impl ApiServer { }; let full_path = format!("{}", root.clone()); self.router.route(op.to_method(), full_path.clone(), wrapper, route_name); - info!("POST {}", full_path); + info!("route: POST {}", full_path); } else { // regular REST operations @@ -264,7 +265,7 @@ impl ApiServer { }; let wrapper = ApiWrapper(endpoint.clone()); self.router.route(op.to_method(), full_path.clone(), wrapper, route_name); - info!("{} {}", op.to_method(), full_path); + info!("route: {} {}", op.to_method(), full_path); } } diff --git a/chain/Cargo.toml b/chain/Cargo.toml index 0719d3caf..ea776c5b7 100644 --- a/chain/Cargo.toml +++ b/chain/Cargo.toml @@ -8,8 +8,8 @@ workspace = ".." bitflags = "^0.7.0" byteorder = "^0.5" log = "^0.3" -serde = "~0.9.10" -serde_derive = "~0.9.10" +serde = "~1.0.8" +serde_derive = "~1.0.8" time = "^0.1" tokio-io = "0.1.1" bytes = "0.4.2" diff --git a/chain/src/store.rs b/chain/src/store.rs index a18e9aa40..bb069c39a 100644 --- a/chain/src/store.rs +++ b/chain/src/store.rs @@ -30,7 +30,6 @@ const BLOCK_PREFIX: u8 = 'b' as u8; const HEAD_PREFIX: u8 = 'H' as u8; const HEADER_HEAD_PREFIX: u8 = 'I' as u8; const HEADER_HEIGHT_PREFIX: u8 = '8' as u8; -const OUTPUT_PREFIX: u8 = 'O' as u8; const OUTPUT_COMMIT_PREFIX: u8 = 'o' as u8; /// An implementation of the ChainStore trait backed by a simple key-value @@ -104,11 +103,10 @@ impl ChainStore for ChainKVStore { // saving the full output under its hash, as well as a commitment to hash index for out in &b.outputs { + let mut out_bytes = out.commit.as_ref().to_vec(); + println!("OUTSAVE: {:?}", out_bytes); batch = batch.put_enc(&mut BlockCodec::default(), - &to_key(OUTPUT_PREFIX, &mut out.hash().to_vec())[..], - out.clone())? - .put_enc(&mut BlockCodec::default(), - &to_key(OUTPUT_COMMIT_PREFIX, &mut out.commit.as_ref().to_vec())[..], + &to_key(OUTPUT_COMMIT_PREFIX, &mut out_bytes)[..], out.hash().clone())?; } batch.write() @@ -125,9 +123,9 @@ impl ChainStore for ChainKVStore { &u64_to_key(HEADER_HEIGHT_PREFIX, height))) } - fn get_output(&self, h: &Hash) -> Result { + fn get_output_by_commit(&self, commit: &Commitment) -> Result { option_to_not_found(self.db.get_dec(&mut BlockCodec::default(), - &to_key(OUTPUT_PREFIX, &mut h.to_vec()))) + &to_key(OUTPUT_COMMIT_PREFIX, &mut commit.as_ref().to_vec()))) } fn has_output_commit(&self, commit: &Commitment) -> Result { diff --git a/chain/src/types.rs b/chain/src/types.rs index 49a9b2edb..28669ca12 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -99,8 +99,8 @@ pub trait ChainStore: Send + Sync { /// Gets the block header at the provided height fn get_header_by_height(&self, height: u64) -> Result; - /// Gets an output by its hash - fn get_output(&self, h: &Hash) -> Result; + /// Gets an output by its commitment + fn get_output_by_commit(&self, commit: &Commitment) -> Result; /// Checks whether an output commitment exists and returns the output hash fn has_output_commit(&self, commit: &Commitment) -> Result; diff --git a/core/Cargo.toml b/core/Cargo.toml index 5c772b3c5..2531fac1f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,8 +10,8 @@ byteorder = "^0.5" num-bigint = "^0.1.35" rust-crypto = "^0.2" rand = "^0.3" -serde = "~0.9.10" -serde_derive = "~0.9.10" +serde = "~1.0.8" +serde_derive = "~1.0.8" time = "^0.1" tiny-keccak = "1.1" diff --git a/core/src/core/hash.rs b/core/src/core/hash.rs index 0325f50df..f0085bf3b 100644 --- a/core/src/core/hash.rs +++ b/core/src/core/hash.rs @@ -17,6 +17,7 @@ //! Primary hash function used in the protocol //! +use std::cmp::min; use std::{fmt, ops}; use tiny_keccak::Keccak; use std::convert::AsRef; @@ -47,6 +48,16 @@ impl fmt::Display for Hash { } impl Hash { + /// Builds a Hash from a byte vector. If the vector is too short, it will be + /// completed by zeroes. If it's too long, it will be truncated. + pub fn from_vec(v: Vec) -> Hash { + let mut h = [0; 32]; + for i in 0..min(v.len(), 32) { + h[i] = v[i]; + } + Hash(h) + } + /// Converts the hash to a byte vector pub fn to_vec(&self) -> Vec { self.0.to_vec() diff --git a/core/src/core/target.rs b/core/src/core/target.rs index 40bb0e6e7..a1b6d03f4 100644 --- a/core/src/core/target.rs +++ b/core/src/core/target.rs @@ -105,9 +105,9 @@ impl Serialize for Difficulty { } } -impl Deserialize for Difficulty { +impl<'de> Deserialize<'de> for Difficulty { fn deserialize(deserializer: D) -> Result - where D: Deserializer + where D: Deserializer<'de> { deserializer.deserialize_i32(DiffVisitor) } @@ -115,7 +115,7 @@ impl Deserialize for Difficulty { struct DiffVisitor; -impl de::Visitor for DiffVisitor { +impl<'de> de::Visitor<'de> for DiffVisitor { type Value = Difficulty; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 2c0f8fbd8..12dd2032f 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -269,6 +269,7 @@ impl Input { bitflags! { /// Options for block validation + #[derive(Serialize, Deserialize)] pub flags OutputFeatures: u8 { /// No flags const DEFAULT_OUTPUT = 0b00000000, @@ -279,10 +280,9 @@ bitflags! { /// Output for a transaction, defining the new ownership of coins that are being /// transferred. The commitment is a blinded value for the output while the -/// range -/// proof guarantees the commitment includes a positive value without overflow -/// and the ownership of the private key. -#[derive(Debug, Copy, Clone, PartialEq)] +/// range proof guarantees the commitment includes a positive value without +/// overflow and the ownership of the private key. +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub struct Output { /// Options for an output's structure or use pub features: OutputFeatures, diff --git a/grin/Cargo.toml b/grin/Cargo.toml index b4b3a46cd..8afbdd320 100644 --- a/grin/Cargo.toml +++ b/grin/Cargo.toml @@ -19,8 +19,8 @@ futures-cpupool = "^0.1.3" hyper = { git = "https://github.com/hyperium/hyper" } log = "^0.3" time = "^0.1" -serde = "~0.9.10" -serde_derive = "~0.9.10" +serde = "~1.0.8" +serde_derive = "~1.0.8" tokio-core="^0.1.1" tokio-timer="^0.1.0" rand = "^0.3" diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 32a1e564e..b2f485f2b 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -13,8 +13,8 @@ futures = "^0.1.9" log = "^0.3" net2 = "0.2.0" rand = "^0.3" -serde = "~0.9.10" -serde_derive = "~0.9.10" +serde = "~1.0.8" +serde_derive = "~1.0.8" tokio-core="^0.1.1" tokio-timer="^0.1.0" diff --git a/src/bin/grin.rs b/src/bin/grin.rs index f84002a66..13e076202 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -96,14 +96,24 @@ fn main() { .help("Wallet passphrase used to generate the private key seed") .takes_value(true)) .subcommand(SubCommand::with_name("receive") - .about("Run the wallet in receiving mode"))) + .about("Run the wallet in receiving mode")) + .subcommand(SubCommand::with_name("send") + .about("Builds a transaction to send someone some coins. By default, the transaction will just be printed to stdout. If a destination is provided, the command will attempt to contact the receiver at that address and send the transaction directly.") + .arg(Arg::with_name("amount") + .help("Amount to send in the smallest denomination") + .index(1)) + .arg(Arg::with_name("dest") + .help("Send the transaction to the provided server") + .short("d") + .long("dest") + .takes_value(true)))) .get_matches(); match args.subcommand() { // server commands and options ("server", Some(server_args)) => { server_command(server_args); - }, + } // client commands and options ("client", Some(client_args)) => { @@ -117,12 +127,7 @@ fn main() { // client commands and options ("wallet", Some(wallet_args)) => { - match wallet_args.subcommand() { - ("receive", _) => { - wallet_command(wallet_args); - }, - _ => panic!("Unknown client command, use 'grin help client' for details"), - } + wallet_command(wallet_args); } _ => println!("Unknown command, use 'grin help' for a list of all commands"), @@ -198,6 +203,14 @@ fn wallet_command(wallet_args: &ArgMatches) { error!("Failed to start Grin wallet receiver: {}.", e); }); } + ("send", Some(send_args)) => { + let amount = send_args.value_of("amount").expect("Amount to send required").parse().expect("Could not parse amount as a whole number."); + let mut dest = "stdout"; + if let Some(d) = send_args.value_of("dest") { + dest = d; + } + wallet::issue_send_tx(&key, amount, dest.to_string()).unwrap(); + } _ => panic!("Unknown wallet command, use 'grin help wallet' for details"), } } diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 963c82dd7..6e10a1311 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -9,9 +9,9 @@ byteorder = "1" log = "^0.3" rand = "^0.3" rust-crypto = "^0.2" -serde = "~0.9.10" -serde_derive = "~0.9.10" -serde_json = "~0.9.8" +serde = "~1.0.8" +serde_derive = "~1.0.8" +serde_json = "~1.0.2" grin_api = { path = "../api" } grin_core = { path = "../core" } diff --git a/wallet/src/extkey.rs b/wallet/src/extkey.rs index 512868652..9e100fed0 100644 --- a/wallet/src/extkey.rs +++ b/wallet/src/extkey.rs @@ -63,7 +63,6 @@ impl error::Error for Error { /// To be usable, a secret key should have an amount assigned to it, /// but when the key is derived, the amount is not known and must be /// given. - #[derive(Debug, Clone)] pub struct ExtendedKey { /// Depth of the extended key @@ -79,7 +78,6 @@ pub struct ExtendedKey { } impl ExtendedKey { - /// Creates a new extended key from a serialized one pub fn from_slice(secp: &Secp256k1, slice: &[u8]) -> Result { // TODO change when ser. ext. size is fixed @@ -134,9 +132,9 @@ impl ExtendedKey { let mut fingerprint: [u8; 4] = [0; 4]; let identifier = ext_key.identifier(); (&mut fingerprint).clone_from_slice(&identifier[0..4]); - ext_key.fingerprint = fingerprint; + ext_key.fingerprint = fingerprint; - Ok(ext_key) + Ok(ext_key) } /// Return the identifier of the key, which is the diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 057653fa4..c9c56ec9d 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -29,6 +29,7 @@ extern crate grin_core as core; extern crate grin_util as util; extern crate secp256k1zkp as secp; +mod checker; mod extkey; mod receiver; mod sender; @@ -36,3 +37,4 @@ mod types; pub use extkey::ExtendedKey; pub use receiver::WalletReceiver; +pub use sender::issue_send_tx; diff --git a/wallet/src/receiver.rs b/wallet/src/receiver.rs index eb7456500..f0991aa1d 100644 --- a/wallet/src/receiver.rs +++ b/wallet/src/receiver.rs @@ -63,54 +63,60 @@ use util; /// Amount in request to build a coinbase output. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CbAmount { - amount: u64, + amount: u64, } /// Response to build a coinbase output. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CbData { - output: String, - kernel: String, + output: String, + kernel: String, } /// Component used to receive coins, implements all the receiving end of the /// wallet REST API as well as some of the command-line operations. #[derive(Clone)] pub struct WalletReceiver { - pub key: ExtendedKey, + pub key: ExtendedKey, } impl ApiEndpoint for WalletReceiver { type ID = String; type T = String; - type OP_IN = CbAmount; - type OP_OUT = CbData; + type OP_IN = CbAmount; + type OP_OUT = CbData; fn operations(&self) -> Vec { vec![Operation::Custom("receive_coinbase".to_string())] } fn operation(&self, op: String, input: CbAmount) -> ApiResult { - debug!("Operation {} with amount {}", op, input.amount); - if input.amount == 0 { - return Err(api::Error::Argument(format!("Zero amount not allowed."))) - } - match op.as_str() { - "receive_coinbase" => { - let (out, kern) = receive_coinbase(&self.key, input.amount). - 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)))?; - let kern_bin = ser::ser_vec(&kern). - map_err(|e| api::Error::Internal(format!("Error serializing kernel: {:?}", e)))?; - Ok(CbData { - output: util::to_hex(out_bin), - kernel: util::to_hex(kern_bin), - }) - }, - _ => Err(api::Error::Argument(format!("Unknown operation: {}", op))), - } - } + debug!("Operation {} with amount {}", op, input.amount); + if input.amount == 0 { + return Err(api::Error::Argument(format!("Zero amount not allowed."))); + } + match op.as_str() { + "receive_coinbase" => { + let (out, kern) = + receive_coinbase(&self.key, input.amount).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)) + })?; + let kern_bin = + ser::ser_vec(&kern).map_err(|e| { + api::Error::Internal(format!("Error serializing kernel: {:?}", e)) + })?; + Ok(CbData { + output: util::to_hex(out_bin), + kernel: util::to_hex(kern_bin), + }) + } + _ => Err(api::Error::Argument(format!("Unknown operation: {}", op))), + } + } } /// Build a coinbase output and the corresponding kernel @@ -131,7 +137,8 @@ fn receive_coinbase(ext_key: &ExtendedKey, amount: u64) -> Result<(Output, TxKer }); wallet_data.write()?; - info!("Using child {} for a new coinbase output.", coinbase_key.n_child); + info!("Using child {} for a new coinbase output.", + coinbase_key.n_child); Block::reward_output(ext_key.key, &secp).map_err(&From::from) } diff --git a/wallet/src/sender.rs b/wallet/src/sender.rs index 19601ca72..4207c88eb 100644 --- a/wallet/src/sender.rs +++ b/wallet/src/sender.rs @@ -16,13 +16,15 @@ use std::convert::From; use secp::{self, Secp256k1}; use secp::key::SecretKey; +use checker; use core::core::{Transaction, build}; use extkey::ExtendedKey; use types::*; -fn issue_send_tx(hd_seed: &[u8], amount: u64, dest: String) -> Result<(), Error> { +pub fn issue_send_tx(ext_key: &ExtendedKey, amount: u64, dest: String) -> Result<(), Error> { + checker::refresh_outputs(&WalletConfig::default(), ext_key); - let (tx, blind_sum) = build_send_tx(hd_seed, amount)?; + let (tx, blind_sum) = build_send_tx(ext_key, amount)?; let json_tx = partial_tx_to_json(amount, blind_sum, tx); if dest == "stdout" { println!("{}", dest); @@ -36,10 +38,9 @@ fn issue_send_tx(hd_seed: &[u8], amount: u64, dest: String) -> Result<(), Error> /// Builds a transaction to send to someone from the HD seed associated with the /// wallet and the amount to send. Handles reading through the wallet data file, /// selecting outputs to spend and building the change. -fn build_send_tx(hd_seed: &[u8], amount: u64) -> Result<(Transaction, SecretKey), Error> { +fn build_send_tx(ext_key: &ExtendedKey, amount: u64) -> Result<(Transaction, SecretKey), Error> { // first, rebuild the private key from the seed let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); - let ext_key = ExtendedKey::from_seed(&secp, hd_seed).map_err(|e| Error::Key(e))?; // second, check from our local wallet data for outputs to spend let mut wallet_data = WalletData::read()?; diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 4981a7b8f..45888c585 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -51,6 +51,17 @@ impl From for Error { } } +#[derive(Debug, Clone)] +pub struct WalletConfig { + pub api_http_addr: String, +} + +impl Default for WalletConfig { + fn default() -> WalletConfig { + WalletConfig { api_http_addr: "http://127.0.0.1:13415".to_string() } + } +} + /// Status of an output that's being tracked by the wallet. Can either be /// unconfirmed, spent, unspent, or locked (when it's been used to generate /// a transaction but we don't have confirmation that the transaction was @@ -79,7 +90,7 @@ pub struct OutputData { } impl OutputData { - /// Lock a given output to avoid conflicting use + /// Lock a given output to avoid conflicting use pub fn lock(&mut self) { self.status = OutputStatus::Locked; } @@ -92,32 +103,32 @@ impl OutputData { /// /// TODO optimization so everything isn't O(n) or even O(n^2) /// TODO account for fees +/// TODO write locks so files don't get overwritten #[derive(Serialize, Deserialize, Debug, Clone)] pub struct WalletData { - outputs: Vec, + pub outputs: Vec, } impl WalletData { + /// Read the wallet data or created a brand new one if it doesn't exist yet + pub fn read_or_create() -> Result { + if Path::new(DAT_FILE).exists() { + WalletData::read() + } else { + // just create a new instance, it will get written afterward + Ok(WalletData { outputs: vec![] }) + } + } - /// Read the wallet data or created a brand new one if it doesn't exist yet - pub fn read_or_create() -> Result { - if Path::new(DAT_FILE).exists() { - WalletData::read() - } else { - // just create a new instance, it will get written afterward - Ok(WalletData { outputs: vec![] }) - } - } - - /// Read the wallet data from disk. + /// Read the wallet data from disk. pub fn read() -> Result { - let mut data_file = File::open(DAT_FILE) + let data_file = File::open(DAT_FILE) .map_err(|e| Error::WalletData(format!("Could not open {}: {}", DAT_FILE, e)))?; serde_json::from_reader(data_file) .map_err(|e| Error::WalletData(format!("Error reading {}: {}", DAT_FILE, e))) } - /// Write the wallet data to disk. + /// Write the wallet data to disk. pub fn write(&self) -> Result<(), Error> { let mut data_file = File::create(DAT_FILE) .map_err(|e| Error::WalletData(format!("Could not create {}: {}", DAT_FILE, e)))?; @@ -127,13 +138,14 @@ impl WalletData { .map_err(|e| Error::WalletData(format!("Error writing {}: {}", DAT_FILE, e))) } - /// Append a new output information to the wallet data. + /// Append a new output information to the wallet data. pub fn append_output(&mut self, out: OutputData) { self.outputs.push(out); } - /// Select a subset of unspent outputs to spend in a transaction transferring - /// the provided amount. + /// Select a subset of unspent outputs to spend in a transaction + /// transferring + /// the provided amount. pub fn select(&self, fingerprint: [u8; 4], amount: u64) -> (Vec, i64) { let mut to_spend = vec![]; let mut input_total = 0; @@ -148,7 +160,7 @@ impl WalletData { (to_spend, (input_total as i64) - (amount as i64)) } - /// Next child index when we want to create a new output. + /// Next child index when we want to create a new output. pub fn next_child(&self, fingerprint: [u8; 4]) -> u32 { let mut max_n = 0; for out in &self.outputs {