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:
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user