File-based transaction building (#1317)

* First implementation of file-based transaction send, receive and finalize in libwallet
* Switch from TOML to JSON (needless complication). Plug in send, receive and finalize wallet commands.
* Pretty JSON for response is too large (all whitespace)
This commit is contained in:
Ignotus Peverell
2018-08-07 11:17:33 -07:00
committed by GitHub
parent e383cb594f
commit f0bdb2c8fa
6 changed files with 223 additions and 50 deletions
+1
View File
@@ -14,6 +14,7 @@
//! Functions for building partial transactions to be passed
//! around during an interactive wallet exchange
use rand::thread_rng;
use uuid::Uuid;
+108 -1
View File
@@ -17,13 +17,17 @@
//! vs. functions to interact with someone else)
//! Still experimental, not sure this is the best way to do this
use std::fs::File;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use serde_json as json;
use core::ser;
use keychain::Keychain;
use libtx::slate::Slate;
use libwallet::internal::{tx, updater};
use libwallet::internal::{tx, updater, selection, sigcontext};
use libwallet::types::{
BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletClient, WalletInfo,
};
@@ -173,6 +177,109 @@ where
Ok(slate_out)
}
/// 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).
pub fn file_send_tx(
&mut self,
amount: u64,
minimum_confirmations: u64,
dest: &str,
max_outputs: usize,
selection_strategy_is_use_all: bool,
) -> Result<(), Error> {
let mut w = self.wallet.lock().unwrap();
w.open_with_credentials()?;
let (slate, context, lock_fn) = tx::create_send_tx(
&mut **w,
amount,
minimum_confirmations,
max_outputs,
selection_strategy_is_use_all,
)?;
let mut pub_tx = File::create(dest)?;
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
pub_tx.sync_all()?;
let mut priv_tx = File::create(dest.to_owned() + ".private")?;
priv_tx.write_all(json::to_string(&context).unwrap().as_bytes())?;
priv_tx.sync_all()?;
// lock our inputs
lock_fn(&mut **w)?;
w.close()?;
Ok(())
}
/// 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)?;
let mut wallet = self.wallet.lock().unwrap();
wallet.open_with_credentials()?;
// create an output using the amount in the slate
let (_, mut context, receiver_create_fn) =
selection::build_recipient_output_with_slate(&mut **wallet, &mut slate)?;
// 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(())
}
/// 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.
pub fn file_finalize_tx(
&mut self,
private_tx_file: &str,
receiver_file: &str,
) -> Result<Slate, Error> {
let mut pub_tx_f = File::open(receiver_file)?;
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)?;
let mut priv_tx_f = File::open(private_tx_file)?;
let mut content = String::new();
priv_tx_f.read_to_string(&mut content)?;
let context: sigcontext::Context = json::from_str(&content).map_err(|_| ErrorKind::Format)?;
let mut w = self.wallet.lock().unwrap();
w.open_with_credentials()?;
tx::complete_tx(&mut **w, &mut slate, &context)?;
w.close()?;
Ok(slate)
}
/// 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
+14 -3
View File
@@ -13,12 +13,15 @@
// limitations under the License.
//! Error types for libwallet
use keychain;
use libtx;
use std::fmt::{self, Display};
use std::io;
use failure::{Backtrace, Context, Fail};
use core::core::transaction;
use failure::{Backtrace, Context, Fail};
use keychain;
use libtx;
/// Error definition
#[derive(Debug, Fail)]
@@ -184,6 +187,14 @@ impl From<Context<ErrorKind>> for Error {
}
}
impl From<io::Error> for Error {
fn from(_error: io::Error) -> Error {
Error {
inner: Context::new(ErrorKind::IO),
}
}
}
impl From<keychain::Error> for Error {
fn from(error: keychain::Error) -> Error {
Error {
+1 -1
View File
@@ -17,7 +17,7 @@ use libtx::aggsig;
use util::secp::key::{PublicKey, SecretKey};
use util::secp::{self, Secp256k1};
#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
/// Holds the context for a single aggsig transaction
pub struct Context {
/// Secret key (of which public is shared)