Add file based transaction in owner API (#1484)
* Add file based transaction in owner API * Add finalize tx in owner API * Code cleanup and placed file_receive in correct API * Output an explicit error when http dest seems incorrect * Add doc on send method * Add cancel tx endpoint in owner API * Add dump stored tx endpoint in owner API * Add missing parameters
This commit is contained in:
committed by
Ignotus Peverell
parent
4030b3d2f1
commit
dd1cef2148
+60
-56
@@ -185,15 +185,16 @@ where
|
||||
|
||||
/// 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(
|
||||
pub fn send_tx(
|
||||
&mut self,
|
||||
write_to_disk: bool,
|
||||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
dest: &str,
|
||||
max_outputs: usize,
|
||||
num_change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<Slate, Error> {
|
||||
let mut w = self.wallet.lock().unwrap();
|
||||
w.open_with_credentials()?;
|
||||
|
||||
@@ -205,10 +206,11 @@ where
|
||||
num_change_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()?;
|
||||
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()?;
|
||||
}
|
||||
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
@@ -221,60 +223,19 @@ where
|
||||
// lock our inputs
|
||||
lock_fn(&mut **w, &tx_hex)?;
|
||||
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(())
|
||||
Ok(slate)
|
||||
}
|
||||
|
||||
/// 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, 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)?;
|
||||
|
||||
pub fn finalize_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock().unwrap();
|
||||
w.open_with_credentials()?;
|
||||
|
||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||
tx::complete_tx(&mut **w, &mut slate, &context)?;
|
||||
tx::complete_tx(&mut **w, slate, &context)?;
|
||||
{
|
||||
let mut batch = w.batch()?;
|
||||
batch.delete_private_context(slate.id.as_bytes())?;
|
||||
@@ -282,7 +243,7 @@ where
|
||||
}
|
||||
|
||||
w.close()?;
|
||||
Ok(slate)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Roll back a transaction and all associated outputs with a given
|
||||
@@ -342,7 +303,12 @@ where
|
||||
}
|
||||
|
||||
/// Writes stored transaction data to a given file
|
||||
pub fn dump_stored_tx(&self, tx_id: u32, dest: &str) -> Result<(), Error> {
|
||||
pub fn dump_stored_tx(
|
||||
&self,
|
||||
tx_id: u32,
|
||||
write_to_disk: bool,
|
||||
dest: &str,
|
||||
) -> Result<Transaction, Error> {
|
||||
let (confirmed, tx_hex) = {
|
||||
let mut w = self.wallet.lock().unwrap();
|
||||
w.open_with_credentials()?;
|
||||
@@ -365,10 +331,12 @@ where
|
||||
}
|
||||
let tx_bin = util::from_hex(tx_hex.unwrap()).unwrap();
|
||||
let tx = ser::deserialize::<Transaction>(&mut &tx_bin[..])?;
|
||||
let mut tx_file = File::create(dest)?;
|
||||
tx_file.write_all(json::to_string(&tx).unwrap().as_bytes())?;
|
||||
tx_file.sync_all()?;
|
||||
Ok(())
|
||||
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)
|
||||
}
|
||||
|
||||
/// (Re)Posts a transaction that's already been stored to the chain
|
||||
@@ -498,6 +466,42 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
/// Receive a transaction from a sender
|
||||
pub fn receive_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
||||
let mut w = self.wallet.lock().unwrap();
|
||||
|
||||
@@ -28,6 +28,7 @@ use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
|
||||
use core::core::Transaction;
|
||||
use keychain::Keychain;
|
||||
use libtx::slate::Slate;
|
||||
use libwallet::api::{APIForeign, APIOwner};
|
||||
@@ -229,6 +230,35 @@ where
|
||||
api.retrieve_txs(update_from_node, id)
|
||||
}
|
||||
|
||||
fn dump_stored_tx(
|
||||
&self,
|
||||
req: &Request<Body>,
|
||||
api: APIOwner<T, C, K>,
|
||||
) -> Result<Transaction, Error> {
|
||||
let params = parse_params(req);
|
||||
if let Some(id_string) = params.get("id") {
|
||||
match id_string[0].parse() {
|
||||
Ok(id) => match api.dump_stored_tx(id, false, "") {
|
||||
Ok(tx) => Ok(tx),
|
||||
Err(e) => {
|
||||
error!(LOGGER, "dump_stored_tx: failed with error: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!(LOGGER, "dump_stored_tx: could not parse id: {}", e);
|
||||
Err(ErrorKind::TransactionDumpError(
|
||||
"dump_stored_tx: cannot dump transaction. Could not parse id in request.",
|
||||
).into())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(ErrorKind::TransactionDumpError(
|
||||
"dump_stored_tx: Cannot dump transaction. Missing id param in request.",
|
||||
).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn retrieve_summary_info(
|
||||
&self,
|
||||
req: &Request<Body>,
|
||||
@@ -260,6 +290,7 @@ where
|
||||
"retrieve_summary_info" => json_response(&self.retrieve_summary_info(req, api)?),
|
||||
"node_height" => json_response(&self.node_height(req, api)?),
|
||||
"retrieve_txs" => json_response(&self.retrieve_txs(req, api)?),
|
||||
"dump_stored_tx" => json_response(&self.dump_stored_tx(req, api)?),
|
||||
_ => response(StatusCode::BAD_REQUEST, ""),
|
||||
})
|
||||
}
|
||||
@@ -270,17 +301,77 @@ where
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |args: SendTXArgs| {
|
||||
api.issue_send_tx(
|
||||
args.amount,
|
||||
args.minimum_confirmations,
|
||||
&args.dest,
|
||||
args.max_outputs,
|
||||
args.num_change_outputs,
|
||||
args.selection_strategy_is_use_all,
|
||||
)
|
||||
if args.method == "http" {
|
||||
api.issue_send_tx(
|
||||
args.amount,
|
||||
args.minimum_confirmations,
|
||||
&args.dest,
|
||||
args.max_outputs,
|
||||
args.num_change_outputs,
|
||||
args.selection_strategy_is_use_all,
|
||||
)
|
||||
} else if args.method == "file" {
|
||||
api.send_tx(
|
||||
false,
|
||||
args.amount,
|
||||
args.minimum_confirmations,
|
||||
&args.dest,
|
||||
args.max_outputs,
|
||||
args.num_change_outputs,
|
||||
args.selection_strategy_is_use_all,
|
||||
)
|
||||
} else {
|
||||
error!(LOGGER, "unsupported payment method: {}", args.method);
|
||||
return Err(ErrorKind::ClientCallback("unsupported payment method"))?;
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn finalize_tx(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||
Box::new(
|
||||
parse_body(req).and_then(move |mut slate| match api.finalize_tx(&mut slate) {
|
||||
Ok(_) => ok(slate.clone()),
|
||||
Err(e) => {
|
||||
error!(LOGGER, "finalize_tx: failed with error: {}", e);
|
||||
err(e)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn cancel_tx(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Box<Future<Item = (), Error = Error> + Send> {
|
||||
let params = parse_params(&req);
|
||||
if let Some(id_string) = params.get("id") {
|
||||
Box::new(match id_string[0].parse() {
|
||||
Ok(id) => match api.cancel_tx(id) {
|
||||
Ok(_) => ok(()),
|
||||
Err(e) => {
|
||||
error!(LOGGER, "finalize_tx: failed with error: {}", e);
|
||||
err(e)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!(LOGGER, "finalize_tx: could not parse id: {}", e);
|
||||
err(ErrorKind::TransactionCancellationError(
|
||||
"finalize_tx: cannot cancel transaction. Could not parse id in request.",
|
||||
).into())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Box::new(err(ErrorKind::TransactionCancellationError(
|
||||
"finalize_tx: Cannot cancel transaction. Missing id param in request.",
|
||||
).into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_burn_tx(
|
||||
&self,
|
||||
_req: Request<Body>,
|
||||
@@ -307,6 +398,14 @@ where
|
||||
self.issue_send_tx(req, api)
|
||||
.and_then(|slate| ok(json_response_pretty(&slate))),
|
||||
),
|
||||
"finalize_tx" => Box::new(
|
||||
self.finalize_tx(req, api)
|
||||
.and_then(|slate| ok(json_response_pretty(&slate))),
|
||||
),
|
||||
"cancel_tx" => Box::new(
|
||||
self.cancel_tx(req, api)
|
||||
.and_then(|_| ok(response(StatusCode::OK, ""))),
|
||||
),
|
||||
"issue_burn_tx" => Box::new(
|
||||
self.issue_burn_tx(req, api)
|
||||
.and_then(|_| ok(response(StatusCode::OK, ""))),
|
||||
|
||||
@@ -152,6 +152,10 @@ pub enum ErrorKind {
|
||||
#[fail(display = "Cancellation Error: {}", _0)]
|
||||
TransactionCancellationError(&'static str),
|
||||
|
||||
/// Cancellation error
|
||||
#[fail(display = "Tx dump Error: {}", _0)]
|
||||
TransactionDumpError(&'static str),
|
||||
|
||||
/// Attempt to repost a transaction that's already confirmed
|
||||
#[fail(display = "Transaction already confirmed error")]
|
||||
TransactionAlreadyConfirmed,
|
||||
|
||||
@@ -642,6 +642,8 @@ pub struct SendTXArgs {
|
||||
pub amount: u64,
|
||||
/// minimum confirmations
|
||||
pub minimum_confirmations: u64,
|
||||
/// payment method
|
||||
pub method: String,
|
||||
/// destination url
|
||||
pub dest: String,
|
||||
/// Max number of outputs
|
||||
|
||||
Reference in New Issue
Block a user