Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fundrawtransaction command, fix signing commands #37

Merged
merged 5 commits into from Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 40 additions & 28 deletions client/src/client.rs
Expand Up @@ -17,7 +17,7 @@ use secp256k1;
use serde;
use serde_json;

use bitcoin::{Address, Block, BlockHeader, Transaction};
use bitcoin::{Address, Block, BlockHeader, PrivateKey, Transaction};
use bitcoin_amount::Amount;
use bitcoin_hashes::sha256d;
use log::Level::Trace;
Expand Down Expand Up @@ -57,6 +57,16 @@ fn null() -> serde_json::Value {
serde_json::Value::Null
}

/// Shorthand for an empty serde_json::Value array.
fn empty_arr() -> serde_json::Value {
serde_json::Value::Array(vec![])
}

/// Shorthand for an empty serde_json object.
fn empty_obj() -> serde_json::Value {
serde_json::Value::Object(Default::default())
}

/// Handle default values in the argument list
///
/// Substitute `Value::Null`s with corresponding values from `defaults` table,
Expand Down Expand Up @@ -345,13 +355,7 @@ pub trait RpcApi: Sized {
opt_into_json(include_unsafe)?,
opt_into_json(query_options)?,
];
let defaults = [
into_json(0)?,
into_json(9999999)?,
into_json::<&[Address]>(&[])?,
into_json(true)?,
null(),
];
let defaults = [into_json(0)?, into_json(9999999)?, empty_arr(), into_json(true)?, null()];
self.call("listunspent", handle_defaults(&mut args, &defaults))
}

Expand Down Expand Up @@ -384,11 +388,23 @@ pub trait RpcApi: Sized {
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}

fn fund_raw_transaction<R: RawTx>(
&self,
tx: R,
options: Option<json::FundRawTransactionOptions>,
is_witness: Option<bool>,
) -> Result<json::FundRawTransactionResult> {
let mut args = [tx.raw_hex().into(), opt_into_json(options)?, opt_into_json(is_witness)?];
let defaults = [empty_obj(), null()];
self.call("fundrawtransaction", handle_defaults(&mut args, &defaults))
}

#[deprecated]
fn sign_raw_transaction<R: RawTx>(
&self,
tx: R,
utxos: Option<&[json::SignRawTransactionInput]>,
private_keys: Option<&[&str]>,
private_keys: Option<&[&PrivateKey]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [
Expand All @@ -397,18 +413,25 @@ pub trait RpcApi: Sized {
opt_into_json(private_keys)?,
opt_into_json(sighash_type)?,
];
let defaults = [
into_json::<&[json::SignRawTransactionInput]>(&[])?,
into_json::<&[&str]>(&[])?,
null(),
];
let defaults = [empty_arr(), empty_arr(), null()];
self.call("signrawtransaction", handle_defaults(&mut args, &defaults))
}

fn sign_raw_transaction_with_wallet<R: RawTx>(
&self,
tx: R,
utxos: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [tx.raw_hex().into(), opt_into_json(utxos)?, opt_into_json(sighash_type)?];
let defaults = [empty_arr(), null()];
self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults))
}

fn sign_raw_transaction_with_key<R: RawTx>(
&self,
tx: R,
privkeys: &[&str],
privkeys: &[&PrivateKey],
prevtxs: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
Expand All @@ -418,7 +441,7 @@ pub trait RpcApi: Sized {
opt_into_json(prevtxs)?,
opt_into_json(sighash_type)?,
];
let defaults = [into_json::<&[json::SignRawTransactionInput]>(&[])?, null()];
let defaults = [empty_arr(), null()];
self.call("signrawtransactionwithkey", handle_defaults(&mut args, &defaults))
}

Expand All @@ -430,17 +453,6 @@ pub trait RpcApi: Sized {
self.call("stop", &[])
}

fn sign_raw_transaction_with_wallet<R: RawTx>(
&self,
tx: R,
utxos: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [tx.raw_hex().into(), opt_into_json(utxos)?, opt_into_json(sighash_type)?];
let defaults = [into_json::<&[json::SignRawTransactionInput]>(&[])?, null()];
self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults))
}

fn verify_message(
&self,
address: &Address,
Expand Down Expand Up @@ -607,8 +619,8 @@ impl RpcApi for Client {
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use bitcoin;
use serde_json;

#[test]
fn test_raw_tx() {
Expand Down
51 changes: 46 additions & 5 deletions json/src/lib.rs
Expand Up @@ -362,8 +362,7 @@ pub struct SignRawTransactionResult {
#[serde(with = "::serde_hex")]
pub hex: Vec<u8>,
pub complete: bool,
#[serde(default)]
pub errors: Vec<SignRawTransactionResultError>,
pub errors: Option<Vec<SignRawTransactionResultError>>,
}

impl SignRawTransactionResult {
Expand Down Expand Up @@ -527,7 +526,7 @@ pub struct BlockRef {
// Custom types for input arguments.

#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[serde(rename_all = "kebab-case")]
#[serde(rename_all = "UPPERCASE")]
pub enum EstimateMode {
Unset,
Economical,
Expand Down Expand Up @@ -570,6 +569,48 @@ pub struct CreateRawTransactionInput {
pub sequence: Option<u32>,
}

#[derive(Serialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "camelCase")]
pub struct FundRawTransactionOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub change_address: Option<Address>,
#[serde(skip_serializing_if = "Option::is_none")]
pub change_position: Option<u32>,
#[serde(rename = "change_type", skip_serializing_if = "Option::is_none")]
pub change_type: Option<AddressType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_watching: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lock_unspents: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fee_rate: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subtract_fee_from_outputs: Option<Vec<u32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub replaceable: Option<bool>,
#[serde(rename = "conf_target", skip_serializing_if = "Option::is_none")]
pub conf_target: Option<u32>,
#[serde(rename = "estimate_mode", skip_serializing_if = "Option::is_none")]
pub estimate_mode: Option<EstimateMode>,
}

#[derive(Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "camelCase")]
pub struct FundRawTransactionResult {
#[serde(with = "::serde_hex")]
pub hex: Vec<u8>,
#[serde(deserialize_with = "deserialize_amount")]
pub fee: Amount,
#[serde(rename = "changepos")]
pub change_position: u32,
}

impl FundRawTransactionResult {
pub fn transaction(&self) -> Result<Transaction, encode::Error> {
encode::deserialize(&self.hex)
}
}

// Used for signrawtransaction argument.
#[derive(Serialize, Clone, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
Expand All @@ -579,11 +620,11 @@ pub struct SignRawTransactionInput {
pub script_pub_key: Script,
#[serde(skip_serializing_if = "Option::is_none")]
pub redeem_script: Option<Script>,
pub amount: f64,
pub amount: Option<f64>,
}

/// Used to represent an address type.
#[derive(Serialize, Clone, PartialEq, Debug)]
#[derive(Serialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "kebab-case")]
pub enum AddressType {
Legacy,
Expand Down