2018-06-06 17:36:29 +03:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
//! Wrappers around library functions, intended to split functions
|
|
|
|
//! into external and internal APIs (i.e. functions for the local wallet
|
|
|
|
//! vs. functions to interact with someone else)
|
|
|
|
//! Still experimental, not sure this is the best way to do this
|
|
|
|
|
2018-08-07 21:17:33 +03:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{Read, Write};
|
2018-06-08 08:21:54 +03:00
|
|
|
use std::marker::PhantomData;
|
2018-10-20 03:13:07 +03:00
|
|
|
use std::sync::Arc;
|
|
|
|
use util::Mutex;
|
2018-06-08 08:21:54 +03:00
|
|
|
|
2018-08-07 21:17:33 +03:00
|
|
|
use serde_json as json;
|
|
|
|
|
2018-08-20 16:48:05 +03:00
|
|
|
use core::core::hash::Hashed;
|
2018-09-05 14:12:29 +03:00
|
|
|
use core::core::Transaction;
|
2018-06-14 15:16:14 +03:00
|
|
|
use core::ser;
|
2018-10-10 12:11:01 +03:00
|
|
|
use keychain::{Identifier, Keychain};
|
2018-06-06 17:36:29 +03:00
|
|
|
use libtx::slate::Slate;
|
2018-10-10 12:11:01 +03:00
|
|
|
use libwallet::internal::{keys, selection, tx, updater};
|
2018-07-09 20:01:19 +03:00
|
|
|
use libwallet::types::{
|
2018-10-10 12:11:01 +03:00
|
|
|
AcctPathMapping, BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend,
|
|
|
|
WalletClient, WalletInfo,
|
2018-07-09 20:01:19 +03:00
|
|
|
};
|
2018-07-20 17:13:37 +03:00
|
|
|
use libwallet::{Error, ErrorKind};
|
2018-10-21 23:30:56 +03:00
|
|
|
use util;
|
2018-08-17 14:15:06 +03:00
|
|
|
use util::secp::pedersen;
|
2018-06-06 17:36:29 +03:00
|
|
|
|
|
|
|
/// Wrapper around internal API functions, containing a reference to
|
|
|
|
/// the wallet/keychain that they're acting upon
|
2018-07-12 18:49:37 +03:00
|
|
|
pub struct APIOwner<W: ?Sized, C, K>
|
2018-06-06 17:36:29 +03:00
|
|
|
where
|
2018-07-12 18:49:37 +03:00
|
|
|
W: WalletBackend<C, K>,
|
2018-07-10 11:18:24 +03:00
|
|
|
C: WalletClient,
|
2018-06-08 08:21:54 +03:00
|
|
|
K: Keychain,
|
2018-06-06 17:36:29 +03:00
|
|
|
{
|
|
|
|
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
|
|
|
/// perhaps)
|
2018-07-12 18:49:37 +03:00
|
|
|
pub wallet: Arc<Mutex<Box<W>>>,
|
2018-06-08 08:21:54 +03:00
|
|
|
phantom: PhantomData<K>,
|
2018-07-10 11:18:24 +03:00
|
|
|
phantom_c: PhantomData<C>,
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
impl<W: ?Sized, C, K> APIOwner<W, C, K>
|
2018-06-06 17:36:29 +03:00
|
|
|
where
|
2018-07-12 18:49:37 +03:00
|
|
|
W: WalletBackend<C, K>,
|
2018-07-10 11:18:24 +03:00
|
|
|
C: WalletClient,
|
2018-06-08 08:21:54 +03:00
|
|
|
K: Keychain,
|
2018-06-06 17:36:29 +03:00
|
|
|
{
|
|
|
|
/// Create new API instance
|
2018-07-12 18:49:37 +03:00
|
|
|
pub fn new(wallet_in: Arc<Mutex<Box<W>>>) -> Self {
|
2018-06-08 08:21:54 +03:00
|
|
|
APIOwner {
|
|
|
|
wallet: wallet_in,
|
|
|
|
phantom: PhantomData,
|
2018-07-10 11:18:24 +03:00
|
|
|
phantom_c: PhantomData,
|
2018-06-08 08:21:54 +03:00
|
|
|
}
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to update and retrieve outputs
|
|
|
|
/// Return (whether the outputs were validated against a node, OutputData)
|
2018-07-20 17:13:37 +03:00
|
|
|
/// if tx_id is some then only retrieve outputs for associated transaction
|
2018-06-06 17:36:29 +03:00
|
|
|
pub fn retrieve_outputs(
|
2018-07-12 18:49:37 +03:00
|
|
|
&self,
|
2018-06-06 17:36:29 +03:00
|
|
|
include_spent: bool,
|
2018-06-13 23:58:45 +03:00
|
|
|
refresh_from_node: bool,
|
2018-07-20 17:13:37 +03:00
|
|
|
tx_id: Option<u32>,
|
2018-08-17 14:15:06 +03:00
|
|
|
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-07-12 18:49:37 +03:00
|
|
|
|
2018-06-13 23:58:45 +03:00
|
|
|
let mut validated = false;
|
|
|
|
if refresh_from_node {
|
2018-07-12 18:49:37 +03:00
|
|
|
validated = self.update_outputs(&mut w);
|
2018-06-13 23:58:45 +03:00
|
|
|
}
|
2018-07-12 18:49:37 +03:00
|
|
|
|
|
|
|
let res = Ok((
|
2018-06-06 17:36:29 +03:00
|
|
|
validated,
|
2018-10-10 12:11:01 +03:00
|
|
|
updater::retrieve_outputs(&mut **w, include_spent, tx_id, &parent_key_id)?,
|
2018-07-12 18:49:37 +03:00
|
|
|
));
|
|
|
|
|
|
|
|
w.close()?;
|
|
|
|
res
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-09-03 14:09:28 +03:00
|
|
|
/// Attempt to update outputs and retrieve transactions
|
2018-07-19 12:35:36 +03:00
|
|
|
/// Return (whether the outputs were validated against a node, OutputData)
|
2018-07-20 17:13:37 +03:00
|
|
|
pub fn retrieve_txs(
|
|
|
|
&self,
|
|
|
|
refresh_from_node: bool,
|
|
|
|
tx_id: Option<u32>,
|
|
|
|
) -> Result<(bool, Vec<TxLogEntry>), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-19 12:35:36 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-07-19 12:35:36 +03:00
|
|
|
|
|
|
|
let mut validated = false;
|
|
|
|
if refresh_from_node {
|
|
|
|
validated = self.update_outputs(&mut w);
|
|
|
|
}
|
|
|
|
|
2018-10-10 12:11:01 +03:00
|
|
|
let res = Ok((
|
|
|
|
validated,
|
|
|
|
updater::retrieve_txs(&mut **w, tx_id, &parent_key_id)?,
|
|
|
|
));
|
2018-07-19 12:35:36 +03:00
|
|
|
|
|
|
|
w.close()?;
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:36:29 +03:00
|
|
|
/// Retrieve summary info for wallet
|
2018-06-14 19:02:05 +03:00
|
|
|
pub fn retrieve_summary_info(
|
|
|
|
&mut self,
|
|
|
|
refresh_from_node: bool,
|
|
|
|
) -> Result<(bool, WalletInfo), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-07-12 18:49:37 +03:00
|
|
|
|
2018-06-13 23:58:45 +03:00
|
|
|
let mut validated = false;
|
|
|
|
if refresh_from_node {
|
2018-07-12 18:49:37 +03:00
|
|
|
validated = self.update_outputs(&mut w);
|
2018-06-13 23:58:45 +03:00
|
|
|
}
|
2018-07-12 18:49:37 +03:00
|
|
|
|
2018-10-10 12:11:01 +03:00
|
|
|
let wallet_info = updater::retrieve_info(&mut **w, &parent_key_id)?;
|
2018-07-12 18:49:37 +03:00
|
|
|
let res = Ok((validated, wallet_info));
|
|
|
|
|
|
|
|
w.close()?;
|
|
|
|
res
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-10-10 12:11:01 +03:00
|
|
|
/// Return list of existing account -> Path mappings
|
|
|
|
pub fn accounts(&mut self) -> Result<Vec<AcctPathMapping>, Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-10-10 12:11:01 +03:00
|
|
|
keys::accounts(&mut **w)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new account path
|
|
|
|
pub fn new_account_path(&mut self, label: &str) -> Result<Identifier, Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-10-10 12:11:01 +03:00
|
|
|
keys::new_acct_path(&mut **w, label)
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:36:29 +03:00
|
|
|
/// Issues a send transaction and sends to recipient
|
|
|
|
pub fn issue_send_tx(
|
|
|
|
&mut self,
|
|
|
|
amount: u64,
|
|
|
|
minimum_confirmations: u64,
|
|
|
|
dest: &str,
|
|
|
|
max_outputs: usize,
|
2018-08-19 00:38:48 +03:00
|
|
|
num_change_outputs: usize,
|
2018-06-06 17:36:29 +03:00
|
|
|
selection_strategy_is_use_all: bool,
|
2018-07-12 18:49:37 +03:00
|
|
|
) -> Result<Slate, Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-07-12 18:49:37 +03:00
|
|
|
|
|
|
|
let client;
|
|
|
|
let mut slate_out: Slate;
|
|
|
|
let lock_fn_out;
|
2018-07-13 17:27:16 +03:00
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
client = w.client().clone();
|
2018-06-07 17:04:21 +03:00
|
|
|
let (slate, context, lock_fn) = tx::create_send_tx(
|
2018-07-12 18:49:37 +03:00
|
|
|
&mut **w,
|
2018-06-06 17:36:29 +03:00
|
|
|
amount,
|
|
|
|
minimum_confirmations,
|
|
|
|
max_outputs,
|
2018-08-19 00:38:48 +03:00
|
|
|
num_change_outputs,
|
2018-06-06 17:36:29 +03:00
|
|
|
selection_strategy_is_use_all,
|
2018-10-10 12:11:01 +03:00
|
|
|
&parent_key_id,
|
2018-11-08 12:46:09 +03:00
|
|
|
false,
|
2018-06-07 17:04:21 +03:00
|
|
|
)?;
|
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
lock_fn_out = lock_fn;
|
2018-07-19 12:35:36 +03:00
|
|
|
slate_out = match client.send_tx_slate(dest, &slate) {
|
2018-06-07 17:04:21 +03:00
|
|
|
Ok(s) => s,
|
|
|
|
Err(e) => {
|
|
|
|
error!(
|
2018-07-12 18:49:37 +03:00
|
|
|
"Communication with receiver failed on SenderInitiation send. Aborting transaction {:?}",
|
|
|
|
e,
|
|
|
|
);
|
2018-06-07 17:04:21 +03:00
|
|
|
return Err(e)?;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
tx::complete_tx(&mut **w, &mut slate_out, &context)?;
|
2018-09-05 14:12:29 +03:00
|
|
|
let tx_hex = util::to_hex(ser::ser_vec(&slate_out.tx).unwrap());
|
2018-06-07 17:04:21 +03:00
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
// lock our inputs
|
2018-09-05 14:12:29 +03:00
|
|
|
lock_fn_out(&mut **w, &tx_hex)?;
|
2018-07-12 18:49:37 +03:00
|
|
|
w.close()?;
|
|
|
|
Ok(slate_out)
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-11-08 12:46:09 +03:00
|
|
|
/// Issues a send transaction to the same wallet, without needing communication
|
|
|
|
/// good for consolidating outputs, or can be extended to split outputs to multiple
|
|
|
|
/// accounts
|
|
|
|
pub fn issue_self_tx(
|
|
|
|
&mut self,
|
|
|
|
amount: u64,
|
|
|
|
minimum_confirmations: u64,
|
|
|
|
max_outputs: usize,
|
|
|
|
num_change_outputs: usize,
|
|
|
|
selection_strategy_is_use_all: bool,
|
|
|
|
src_acct_name: &str,
|
|
|
|
dest_acct_name: &str,
|
|
|
|
) -> Result<Slate, Error> {
|
|
|
|
let mut w = self.wallet.lock();
|
|
|
|
w.open_with_credentials()?;
|
|
|
|
let orig_parent_key_id = w.parent_key_id();
|
|
|
|
w.set_parent_key_id_by_name(src_acct_name)?;
|
|
|
|
let parent_key_id = w.parent_key_id();
|
|
|
|
|
|
|
|
let (mut slate, context, lock_fn) = tx::create_send_tx(
|
|
|
|
&mut **w,
|
|
|
|
amount,
|
|
|
|
minimum_confirmations,
|
|
|
|
max_outputs,
|
|
|
|
num_change_outputs,
|
|
|
|
selection_strategy_is_use_all,
|
|
|
|
&parent_key_id,
|
|
|
|
true,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
w.set_parent_key_id_by_name(dest_acct_name)?;
|
|
|
|
let parent_key_id = w.parent_key_id();
|
|
|
|
tx::receive_tx(&mut **w, &mut slate, &parent_key_id, true)?;
|
|
|
|
|
|
|
|
tx::complete_tx(&mut **w, &mut slate, &context)?;
|
|
|
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
|
|
|
|
|
|
|
// lock our inputs
|
|
|
|
lock_fn(&mut **w, &tx_hex)?;
|
|
|
|
w.set_parent_key_id(orig_parent_key_id);
|
|
|
|
w.close()?;
|
|
|
|
Ok(slate)
|
|
|
|
}
|
|
|
|
|
2018-08-07 21:17:33 +03:00
|
|
|
/// Write a transaction to send to file so a user can transmit it to the
|
|
|
|
/// receiver in whichever way they see fit (aka carrier pigeon mode).
|
2018-09-11 05:18:10 +03:00
|
|
|
pub fn send_tx(
|
2018-08-07 21:17:33 +03:00
|
|
|
&mut self,
|
2018-09-11 05:18:10 +03:00
|
|
|
write_to_disk: bool,
|
2018-08-07 21:17:33 +03:00
|
|
|
amount: u64,
|
|
|
|
minimum_confirmations: u64,
|
|
|
|
dest: &str,
|
|
|
|
max_outputs: usize,
|
2018-08-19 00:38:48 +03:00
|
|
|
num_change_outputs: usize,
|
2018-08-07 21:17:33 +03:00
|
|
|
selection_strategy_is_use_all: bool,
|
2018-09-11 05:18:10 +03:00
|
|
|
) -> Result<Slate, Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-08-07 21:17:33 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-08-07 21:17:33 +03:00
|
|
|
|
|
|
|
let (slate, context, lock_fn) = tx::create_send_tx(
|
|
|
|
&mut **w,
|
|
|
|
amount,
|
|
|
|
minimum_confirmations,
|
|
|
|
max_outputs,
|
2018-08-19 00:38:48 +03:00
|
|
|
num_change_outputs,
|
2018-08-07 21:17:33 +03:00
|
|
|
selection_strategy_is_use_all,
|
2018-10-10 12:11:01 +03:00
|
|
|
&parent_key_id,
|
2018-11-08 12:46:09 +03:00
|
|
|
false,
|
2018-08-07 21:17:33 +03:00
|
|
|
)?;
|
2018-09-11 05:18:10 +03:00
|
|
|
if write_to_disk {
|
|
|
|
let mut pub_tx = File::create(dest)?;
|
|
|
|
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
|
|
|
|
pub_tx.sync_all()?;
|
|
|
|
}
|
2018-08-28 03:57:33 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
let mut batch = w.batch()?;
|
|
|
|
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
|
|
|
batch.commit()?;
|
|
|
|
}
|
2018-08-07 21:17:33 +03:00
|
|
|
|
2018-09-05 14:12:29 +03:00
|
|
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
|
|
|
|
2018-08-07 21:17:33 +03:00
|
|
|
// lock our inputs
|
2018-09-05 14:12:29 +03:00
|
|
|
lock_fn(&mut **w, &tx_hex)?;
|
2018-08-07 21:17:33 +03:00
|
|
|
w.close()?;
|
2018-09-11 05:18:10 +03:00
|
|
|
Ok(slate)
|
2018-08-07 21:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sender finalization of the transaction. Takes the file returned by the
|
|
|
|
/// sender as well as the private file generate on the first send step.
|
|
|
|
/// Builds the complete transaction and sends it to a grin node for
|
|
|
|
/// propagation.
|
2018-09-11 05:18:10 +03:00
|
|
|
pub fn finalize_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-08-07 21:17:33 +03:00
|
|
|
w.open_with_credentials()?;
|
|
|
|
|
2018-08-28 03:57:33 +03:00
|
|
|
let context = w.get_private_context(slate.id.as_bytes())?;
|
2018-09-11 05:18:10 +03:00
|
|
|
tx::complete_tx(&mut **w, slate, &context)?;
|
2018-08-28 03:57:33 +03:00
|
|
|
{
|
|
|
|
let mut batch = w.batch()?;
|
|
|
|
batch.delete_private_context(slate.id.as_bytes())?;
|
|
|
|
batch.commit()?;
|
|
|
|
}
|
2018-08-07 21:17:33 +03:00
|
|
|
|
|
|
|
w.close()?;
|
2018-09-11 05:18:10 +03:00
|
|
|
Ok(())
|
2018-08-07 21:17:33 +03:00
|
|
|
}
|
|
|
|
|
2018-07-20 17:13:37 +03:00
|
|
|
/// Roll back a transaction and all associated outputs with a given
|
|
|
|
/// transaction id This means delete all change outputs, (or recipient
|
|
|
|
/// output if you're recipient), and unlock all locked outputs associated
|
|
|
|
/// with the transaction used when a transaction is created but never
|
|
|
|
/// posted
|
|
|
|
pub fn cancel_tx(&mut self, tx_id: u32) -> Result<(), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-20 17:13:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-07-20 17:13:37 +03:00
|
|
|
if !self.update_outputs(&mut w) {
|
|
|
|
return Err(ErrorKind::TransactionCancellationError(
|
|
|
|
"Can't contact running Grin node. Not Cancelling.",
|
|
|
|
))?;
|
|
|
|
}
|
2018-10-10 12:11:01 +03:00
|
|
|
tx::cancel_tx(&mut **w, &parent_key_id, tx_id)?;
|
2018-07-20 17:13:37 +03:00
|
|
|
w.close()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:36:29 +03:00
|
|
|
/// Issue a burn TX
|
|
|
|
pub fn issue_burn_tx(
|
|
|
|
&mut self,
|
|
|
|
amount: u64,
|
|
|
|
minimum_confirmations: u64,
|
|
|
|
max_outputs: usize,
|
|
|
|
) -> Result<(), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
|
|
|
let tx_burn = tx::issue_burn_tx(
|
|
|
|
&mut **w,
|
|
|
|
amount,
|
|
|
|
minimum_confirmations,
|
|
|
|
max_outputs,
|
|
|
|
&parent_key_id,
|
|
|
|
)?;
|
2018-06-07 17:04:21 +03:00
|
|
|
let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap());
|
2018-07-12 18:49:37 +03:00
|
|
|
w.client().post_tx(&TxWrapper { tx_hex: tx_hex }, false)?;
|
|
|
|
w.close()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Posts a transaction to the chain
|
|
|
|
pub fn post_tx(&self, slate: &Slate, fluff: bool) -> Result<(), Error> {
|
|
|
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
|
|
|
let client = {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.client().clone()
|
|
|
|
};
|
2018-08-20 16:48:05 +03:00
|
|
|
let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff);
|
|
|
|
if let Err(e) = res {
|
2018-10-21 23:30:56 +03:00
|
|
|
error!("api: post_tx: failed with error: {}", e);
|
2018-08-20 16:48:05 +03:00
|
|
|
Err(e)
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"api: post_tx: successfully posted tx: {}, fluff? {}",
|
|
|
|
slate.tx.hash(),
|
|
|
|
fluff
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-09-05 14:12:29 +03:00
|
|
|
/// Writes stored transaction data to a given file
|
2018-09-11 05:18:10 +03:00
|
|
|
pub fn dump_stored_tx(
|
|
|
|
&self,
|
|
|
|
tx_id: u32,
|
|
|
|
write_to_disk: bool,
|
|
|
|
dest: &str,
|
|
|
|
) -> Result<Transaction, Error> {
|
2018-09-05 14:12:29 +03:00
|
|
|
let (confirmed, tx_hex) = {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-09-05 14:12:29 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
|
|
|
let res = tx::retrieve_tx_hex(&mut **w, &parent_key_id, tx_id)?;
|
2018-09-05 14:12:29 +03:00
|
|
|
w.close()?;
|
|
|
|
res
|
|
|
|
};
|
|
|
|
if confirmed {
|
|
|
|
warn!(
|
2018-10-21 23:30:56 +03:00
|
|
|
"api: dump_stored_tx: transaction at {} is already confirmed.",
|
|
|
|
tx_id
|
2018-09-05 14:12:29 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if tx_hex.is_none() {
|
|
|
|
error!(
|
2018-10-21 23:30:56 +03:00
|
|
|
"api: dump_stored_tx: completed transaction at {} does not exist.",
|
|
|
|
tx_id
|
2018-09-05 14:12:29 +03:00
|
|
|
);
|
|
|
|
return Err(ErrorKind::TransactionBuildingNotCompleted(tx_id))?;
|
|
|
|
}
|
|
|
|
let tx_bin = util::from_hex(tx_hex.unwrap()).unwrap();
|
|
|
|
let tx = ser::deserialize::<Transaction>(&mut &tx_bin[..])?;
|
2018-09-11 05:18:10 +03:00
|
|
|
if write_to_disk {
|
|
|
|
let mut tx_file = File::create(dest)?;
|
|
|
|
tx_file.write_all(json::to_string(&tx).unwrap().as_bytes())?;
|
|
|
|
tx_file.sync_all()?;
|
|
|
|
}
|
|
|
|
Ok(tx)
|
2018-09-05 14:12:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// (Re)Posts a transaction that's already been stored to the chain
|
|
|
|
pub fn post_stored_tx(&self, tx_id: u32, fluff: bool) -> Result<(), Error> {
|
|
|
|
let client;
|
|
|
|
let (confirmed, tx_hex) = {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-09-05 14:12:29 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-09-05 14:12:29 +03:00
|
|
|
client = w.client().clone();
|
2018-10-10 12:11:01 +03:00
|
|
|
let res = tx::retrieve_tx_hex(&mut **w, &parent_key_id, tx_id)?;
|
2018-09-05 14:12:29 +03:00
|
|
|
w.close()?;
|
|
|
|
res
|
|
|
|
};
|
|
|
|
if confirmed {
|
|
|
|
error!(
|
2018-10-21 23:30:56 +03:00
|
|
|
"api: repost_tx: transaction at {} is confirmed. NOT resending.",
|
|
|
|
tx_id
|
2018-09-05 14:12:29 +03:00
|
|
|
);
|
|
|
|
return Err(ErrorKind::TransactionAlreadyConfirmed)?;
|
|
|
|
}
|
|
|
|
if tx_hex.is_none() {
|
|
|
|
error!(
|
2018-10-21 23:30:56 +03:00
|
|
|
"api: repost_tx: completed transaction at {} does not exist.",
|
|
|
|
tx_id
|
2018-09-05 14:12:29 +03:00
|
|
|
);
|
|
|
|
return Err(ErrorKind::TransactionBuildingNotCompleted(tx_id))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let res = client.post_tx(
|
|
|
|
&TxWrapper {
|
|
|
|
tx_hex: tx_hex.unwrap(),
|
|
|
|
},
|
|
|
|
fluff,
|
|
|
|
);
|
|
|
|
if let Err(e) = res {
|
2018-10-21 23:30:56 +03:00
|
|
|
error!("api: repost_tx: failed with error: {}", e);
|
2018-09-05 14:12:29 +03:00
|
|
|
Err(e)
|
|
|
|
} else {
|
|
|
|
debug!(
|
2018-10-21 23:30:56 +03:00
|
|
|
"api: repost_tx: successfully posted tx at: {}, fluff? {}",
|
|
|
|
tx_id, fluff
|
2018-09-05 14:12:29 +03:00
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:36:29 +03:00
|
|
|
/// Attempt to restore contents of wallet
|
|
|
|
pub fn restore(&mut self) -> Result<(), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
|
|
|
let res = w.restore();
|
|
|
|
w.close()?;
|
|
|
|
res
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve current height from node
|
|
|
|
pub fn node_height(&mut self) -> Result<(u64, bool), Error> {
|
2018-07-19 12:35:36 +03:00
|
|
|
let res = {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-19 12:35:36 +03:00
|
|
|
w.open_with_credentials()?;
|
|
|
|
w.client().get_chain_height()
|
|
|
|
};
|
2018-07-12 18:49:37 +03:00
|
|
|
match res {
|
2018-10-10 12:11:01 +03:00
|
|
|
Ok(height) => Ok((height, true)),
|
2018-06-06 17:36:29 +03:00
|
|
|
Err(_) => {
|
2018-07-20 17:13:37 +03:00
|
|
|
let outputs = self.retrieve_outputs(true, false, None)?;
|
2018-08-17 14:15:06 +03:00
|
|
|
let height = match outputs.1.iter().map(|(out, _)| out.height).max() {
|
2018-06-06 17:36:29 +03:00
|
|
|
Some(height) => height,
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
Ok((height, false))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to update outputs in wallet, return whether it was successful
|
2018-07-13 17:27:16 +03:00
|
|
|
fn update_outputs(&self, w: &mut W) -> bool {
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
|
|
|
match updater::refresh_outputs(&mut *w, &parent_key_id) {
|
2018-06-06 17:36:29 +03:00
|
|
|
Ok(_) => true,
|
|
|
|
Err(_) => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Wrapper around external API functions, intended to communicate
|
|
|
|
/// with other parties
|
2018-07-12 18:49:37 +03:00
|
|
|
pub struct APIForeign<W: ?Sized, C, K>
|
2018-06-06 17:36:29 +03:00
|
|
|
where
|
2018-07-12 18:49:37 +03:00
|
|
|
W: WalletBackend<C, K>,
|
2018-07-10 11:18:24 +03:00
|
|
|
C: WalletClient,
|
2018-06-08 08:21:54 +03:00
|
|
|
K: Keychain,
|
2018-06-06 17:36:29 +03:00
|
|
|
{
|
|
|
|
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
|
|
|
/// perhaps)
|
2018-07-12 18:49:37 +03:00
|
|
|
pub wallet: Arc<Mutex<Box<W>>>,
|
2018-06-08 08:21:54 +03:00
|
|
|
phantom: PhantomData<K>,
|
2018-07-10 11:18:24 +03:00
|
|
|
phantom_c: PhantomData<C>,
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-07-12 18:49:37 +03:00
|
|
|
impl<'a, W: ?Sized, C, K> APIForeign<W, C, K>
|
2018-06-06 17:36:29 +03:00
|
|
|
where
|
2018-07-10 11:18:24 +03:00
|
|
|
W: WalletBackend<C, K>,
|
|
|
|
C: WalletClient,
|
2018-06-08 08:21:54 +03:00
|
|
|
K: Keychain,
|
2018-06-06 17:36:29 +03:00
|
|
|
{
|
|
|
|
/// Create new API instance
|
2018-07-12 18:49:37 +03:00
|
|
|
pub fn new(wallet_in: Arc<Mutex<Box<W>>>) -> Box<Self> {
|
|
|
|
Box::new(APIForeign {
|
2018-06-08 08:21:54 +03:00
|
|
|
wallet: wallet_in,
|
|
|
|
phantom: PhantomData,
|
2018-07-10 11:18:24 +03:00
|
|
|
phantom_c: PhantomData,
|
2018-07-12 18:49:37 +03:00
|
|
|
})
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Build a new (potential) coinbase transaction in the wallet
|
|
|
|
pub fn build_coinbase(&mut self, block_fees: &BlockFees) -> Result<CbData, Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
|
|
|
let res = updater::build_coinbase(&mut **w, block_fees);
|
|
|
|
w.close()?;
|
|
|
|
res
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
|
2018-09-11 05:18:10 +03:00
|
|
|
/// A sender provided a transaction file with appropriate public keys and
|
|
|
|
/// metadata. Complete the receivers' end of it to generate another file
|
|
|
|
/// to send back.
|
|
|
|
pub fn file_receive_tx(&mut self, source: &str) -> Result<(), Error> {
|
|
|
|
let mut pub_tx_f = File::open(source)?;
|
|
|
|
let mut content = String::new();
|
|
|
|
pub_tx_f.read_to_string(&mut content)?;
|
|
|
|
let mut slate: Slate = json::from_str(&content).map_err(|_| ErrorKind::Format)?;
|
|
|
|
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut wallet = self.wallet.lock();
|
2018-09-11 05:18:10 +03:00
|
|
|
wallet.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = wallet.parent_key_id();
|
2018-09-11 05:18:10 +03:00
|
|
|
|
|
|
|
// create an output using the amount in the slate
|
2018-11-08 12:46:09 +03:00
|
|
|
let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(
|
|
|
|
&mut **wallet,
|
|
|
|
&mut slate,
|
|
|
|
parent_key_id,
|
|
|
|
false,
|
|
|
|
)?;
|
2018-09-11 05:18:10 +03:00
|
|
|
|
|
|
|
// fill public keys
|
|
|
|
let _ = slate.fill_round_1(
|
|
|
|
wallet.keychain(),
|
|
|
|
&mut context.sec_key,
|
|
|
|
&context.sec_nonce,
|
|
|
|
1,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// perform partial sig
|
|
|
|
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
|
|
|
|
|
|
|
|
// save to file
|
|
|
|
let mut pub_tx = File::create(source.to_owned() + ".response")?;
|
|
|
|
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
|
|
|
|
|
|
|
|
// Save output in wallet
|
|
|
|
let _ = receiver_create_fn(&mut wallet);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:36:29 +03:00
|
|
|
/// Receive a transaction from a sender
|
|
|
|
pub fn receive_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
2018-10-20 03:13:07 +03:00
|
|
|
let mut w = self.wallet.lock();
|
2018-07-12 18:49:37 +03:00
|
|
|
w.open_with_credentials()?;
|
2018-10-10 12:11:01 +03:00
|
|
|
let parent_key_id = w.parent_key_id();
|
2018-11-08 12:46:09 +03:00
|
|
|
let res = tx::receive_tx(&mut **w, slate, &parent_key_id, false);
|
2018-07-12 18:49:37 +03:00
|
|
|
w.close()?;
|
2018-08-20 16:48:05 +03:00
|
|
|
|
|
|
|
if let Err(e) = res {
|
2018-10-21 23:30:56 +03:00
|
|
|
error!("api: receive_tx: failed with error: {}", e);
|
2018-08-20 16:48:05 +03:00
|
|
|
Err(e)
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"api: receive_tx: successfully received tx: {}",
|
|
|
|
slate.tx.hash()
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-06-06 17:36:29 +03:00
|
|
|
}
|
|
|
|
}
|