// Copyright 2018 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Generic implementation of owner API functions use uuid::Uuid; use crate::core::core::hash::Hashed; use crate::core::core::Transaction; use crate::core::ser; use crate::internal::{keys, selection, tx, updater}; use crate::keychain::{Identifier, Keychain}; use crate::slate::Slate; use crate::types::{ AcctPathMapping, NodeClient, OutputCommitMapping, TxEstimation, TxLogEntry, TxWrapper, WalletBackend, WalletInfo, }; use crate::{Error, ErrorKind}; const USER_MESSAGE_MAX_LEN: usize = 256; /// List of accounts pub fn accounts(w: &mut T) -> Result, Error> where T: WalletBackend, C: NodeClient, K: Keychain, { keys::accounts(&mut *w) } /// new account path pub fn create_account_path(w: &mut T, label: &str) -> Result where T: WalletBackend, C: NodeClient, K: Keychain, { keys::new_acct_path(&mut *w, label) } /// set active account pub fn set_active_account(w: &mut T, label: &str) -> Result<(), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { w.set_parent_key_id_by_name(label) } /// retrieve outputs pub fn retrieve_outputs( w: &mut T, include_spent: bool, refresh_from_node: bool, tx_id: Option, ) -> Result<(bool, Vec), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = w.parent_key_id(); let mut validated = false; if refresh_from_node { validated = update_outputs(w, false); } Ok(( validated, updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?, )) } /// Retrieve txs pub fn retrieve_txs( w: &mut T, refresh_from_node: bool, tx_id: Option, tx_slate_id: Option, ) -> Result<(bool, Vec), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = w.parent_key_id(); let mut validated = false; if refresh_from_node { validated = update_outputs(w, false); } Ok(( validated, updater::retrieve_txs(&mut *w, tx_id, tx_slate_id, Some(&parent_key_id), false)?, )) } /// Retrieve summary info pub fn retrieve_summary_info( w: &mut T, refresh_from_node: bool, minimum_confirmations: u64, ) -> Result<(bool, WalletInfo), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = w.parent_key_id(); let mut validated = false; if refresh_from_node { validated = update_outputs(w, false); } let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?; Ok((validated, wallet_info)) } /// Initiate tx as sender pub fn initiate_tx( w: &mut T, src_acct_name: Option<&str>, amount: u64, minimum_confirmations: u64, max_outputs: usize, num_change_outputs: usize, selection_strategy_is_use_all: bool, message: Option, target_slate_version: Option, use_test_rng: bool, ) -> Result where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = match src_acct_name { Some(d) => { let pm = w.get_acct_path(d.to_owned())?; match pm { Some(p) => p.path, None => w.parent_key_id(), } } None => w.parent_key_id(), }; let message = match message { Some(mut m) => { m.truncate(USER_MESSAGE_MAX_LEN); Some(m) } None => None, }; let mut slate = tx::new_tx_slate(&mut *w, amount, 2, use_test_rng)?; let context = tx::add_inputs_to_slate( &mut *w, &mut slate, minimum_confirmations, max_outputs, num_change_outputs, selection_strategy_is_use_all, &parent_key_id, 0, message, use_test_rng, )?; // Save the aggsig context in our DB for when we // recieve the transaction back { let mut batch = w.batch()?; batch.save_private_context(slate.id.as_bytes(), &context)?; batch.commit()?; } if let Some(v) = target_slate_version { slate.version_info.orig_version = v; } Ok(slate) } /// Estimate pub fn estimate_initiate_tx( w: &mut T, src_acct_name: Option<&str>, amount: u64, minimum_confirmations: u64, max_outputs: usize, num_change_outputs: usize, selection_strategy_is_use_all: bool, ) -> Result where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = match src_acct_name { Some(d) => { let pm = w.get_acct_path(d.to_owned())?; match pm { Some(p) => p.path, None => w.parent_key_id(), } } None => w.parent_key_id(), }; let (total, fee) = tx::estimate_send_tx( &mut *w, amount, minimum_confirmations, max_outputs, num_change_outputs, selection_strategy_is_use_all, &parent_key_id, )?; Ok(TxEstimation { total, fee }) } /// Lock sender outputs pub fn tx_lock_outputs(w: &mut T, slate: &Slate) -> Result<(), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let context = w.get_private_context(slate.id.as_bytes())?; selection::lock_tx_context(&mut *w, slate, &context) } /// Finalize slate pub fn finalize_tx(w: &mut T, slate: &Slate) -> Result where T: WalletBackend, C: NodeClient, K: Keychain, { let mut sl = slate.clone(); let context = w.get_private_context(sl.id.as_bytes())?; tx::complete_tx(&mut *w, &mut sl, 0, &context)?; tx::update_stored_tx(&mut *w, &mut sl)?; tx::update_message(&mut *w, &mut sl)?; { let mut batch = w.batch()?; batch.delete_private_context(sl.id.as_bytes())?; batch.commit()?; } Ok(sl) } /// cancel tx pub fn cancel_tx( w: &mut T, tx_id: Option, tx_slate_id: Option, ) -> Result<(), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = w.parent_key_id(); if !update_outputs(w, false) { return Err(ErrorKind::TransactionCancellationError( "Can't contact running Grin node. Not Cancelling.", ))?; } tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id) } /// get stored tx pub fn get_stored_tx( w: &T, entry: &TxLogEntry, ) -> Result, Error> where T: WalletBackend, C: NodeClient, K: Keychain, { w.get_stored_tx(entry) } /// Posts a transaction to the chain /// take a client impl instead of wallet so as not to have to lock the wallet pub fn post_tx(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error> where C: NodeClient, { let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap()); let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff); if let Err(e) = res { error!("api: post_tx: failed with error: {}", e); Err(e) } else { debug!( "api: post_tx: successfully posted tx: {}, fluff? {}", tx.hash(), fluff ); Ok(()) } } /// verify slate messages pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> { slate.verify_messages() } /// Attempt to restore contents of wallet pub fn restore(w: &mut T) -> Result<(), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { w.restore() } /// check repair pub fn check_repair(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { update_outputs(w, true); w.check_repair(delete_unconfirmed) } /// node height pub fn node_height(w: &mut T) -> Result<(u64, bool), Error> where T: WalletBackend, C: NodeClient, K: Keychain, { let res = w.w2n_client().get_chain_height(); match res { Ok(height) => Ok((height, true)), Err(_) => { let outputs = retrieve_outputs(w, true, false, None)?; let height = match outputs.1.iter().map(|m| m.output.height).max() { Some(height) => height, None => 0, }; Ok((height, false)) } } } /// Attempt to update outputs in wallet, return whether it was successful fn update_outputs(w: &mut T, update_all: bool) -> bool where T: WalletBackend, C: NodeClient, K: Keychain, { let parent_key_id = w.parent_key_id(); match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) { Ok(_) => true, Err(_) => false, } }