[wallet] Add force_non_witness_utxo() to TxBuilder

This commit is contained in:
Alekos Filini 2020-08-08 12:06:40 +02:00
parent 8d9ccf8d0b
commit c90c752f21
No known key found for this signature in database
GPG Key ID: 5E8AFC3034FDFA4F
6 changed files with 40 additions and 19 deletions

View File

@ -68,7 +68,7 @@ impl OnlineBlockchain for ElectrumBlockchain {
.map(|_| ())?) .map(|_| ())?)
} }
fn get_height(&self) -> Result<usize, Error> { fn get_height(&self) -> Result<u32, Error> {
// TODO: unsubscribe when added to the client, or is there a better call to use here? // TODO: unsubscribe when added to the client, or is there a better call to use here?
Ok(self Ok(self
@ -76,7 +76,7 @@ impl OnlineBlockchain for ElectrumBlockchain {
.as_ref() .as_ref()
.ok_or(Error::OfflineClient)? .ok_or(Error::OfflineClient)?
.block_headers_subscribe() .block_headers_subscribe()
.map(|data| data.height)?) .map(|data| data.height as u32)?)
} }
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> { fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {

View File

@ -93,7 +93,7 @@ impl OnlineBlockchain for EsploraBlockchain {
._broadcast(tx))?) ._broadcast(tx))?)
} }
fn get_height(&self) -> Result<usize, Error> { fn get_height(&self) -> Result<u32, Error> {
Ok(await_or_block!(self Ok(await_or_block!(self
.0 .0
.as_ref() .as_ref()
@ -153,7 +153,7 @@ impl UrlClient {
Ok(()) Ok(())
} }
async fn _get_height(&self) -> Result<usize, EsploraError> { async fn _get_height(&self) -> Result<u32, EsploraError> {
let req = self let req = self
.client .client
.get(&format!("{}/api/blocks/tip/height", self.url)) .get(&format!("{}/api/blocks/tip/height", self.url))

View File

@ -64,7 +64,7 @@ pub trait OnlineBlockchain: Blockchain {
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>; fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>; fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
fn get_height(&self) -> Result<usize, Error>; fn get_height(&self) -> Result<u32, Error>;
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error>; fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error>;
} }

View File

@ -326,8 +326,11 @@ where
.map(|s| parse_addressee(s)) .map(|s| parse_addressee(s))
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.map_err(|s| Error::Generic(s))?; .map_err(|s| Error::Generic(s))?;
let mut tx_builder = let mut tx_builder = TxBuilder::from_addressees(addressees);
TxBuilder::from_addressees(addressees).send_all(sub_matches.is_present("send_all"));
if sub_matches.is_present("send_all") {
tx_builder = tx_builder.send_all();
}
if let Some(fee_rate) = sub_matches.value_of("fee_rate") { if let Some(fee_rate) = sub_matches.value_of("fee_rate") {
let fee_rate = f32::from_str(fee_rate).map_err(|s| Error::Generic(s.to_string()))?; let fee_rate = f32::from_str(fee_rate).map_err(|s| Error::Generic(s.to_string()))?;

View File

@ -1,4 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::ops::DerefMut; use std::ops::DerefMut;
use std::str::FromStr; use std::str::FromStr;
@ -236,6 +237,12 @@ where
fee_amount, fee_amount,
)?; )?;
let (mut txin, prev_script_pubkeys): (Vec<_>, Vec<_>) = txin.into_iter().unzip(); let (mut txin, prev_script_pubkeys): (Vec<_>, Vec<_>) = txin.into_iter().unzip();
// map that allows us to lookup the prev_script_pubkey for a given previous_output
let prev_script_pubkeys = txin
.iter()
.zip(prev_script_pubkeys.into_iter())
.map(|(txin, script)| (txin.previous_output, script))
.collect::<HashMap<_, _>>();
txin.iter_mut().for_each(|i| i.sequence = n_sequence); txin.iter_mut().for_each(|i| i.sequence = n_sequence);
tx.input = txin; tx.input = txin;
@ -285,12 +292,13 @@ where
let mut psbt = PSBT::from_unsigned_tx(tx)?; let mut psbt = PSBT::from_unsigned_tx(tx)?;
// add metadata for the inputs // add metadata for the inputs
for ((psbt_input, prev_script), input) in psbt for (psbt_input, input) in psbt
.inputs .inputs
.iter_mut() .iter_mut()
.zip(prev_script_pubkeys.into_iter())
.zip(psbt.global.unsigned_tx.input.iter()) .zip(psbt.global.unsigned_tx.input.iter())
{ {
let prev_script = prev_script_pubkeys.get(&input.previous_output).unwrap();
// Add sighash, default is obviously "ALL" // Add sighash, default is obviously "ALL"
psbt_input.sighash_type = builder.sighash.or(Some(SigHashType::All)); psbt_input.sighash_type = builder.sighash.or(Some(SigHashType::All));
@ -317,7 +325,8 @@ where
if derived_descriptor.is_witness() { if derived_descriptor.is_witness() {
psbt_input.witness_utxo = psbt_input.witness_utxo =
Some(prev_tx.output[prev_output.vout as usize].clone()); Some(prev_tx.output[prev_output.vout as usize].clone());
} else { }
if !derived_descriptor.is_witness() || builder.force_non_witness_utxo {
psbt_input.non_witness_utxo = Some(prev_tx); psbt_input.non_witness_utxo = Some(prev_tx);
} }
} }
@ -535,7 +544,6 @@ where
n, input.previous_output, create_height, current_height n, input.previous_output, create_height, current_height
); );
// TODO: use height once we sync headers
let satisfier = let satisfier =
PSBTSatisfier::new(&psbt.inputs[n], false, create_height, current_height); PSBTSatisfier::new(&psbt.inputs[n], false, create_height, current_height);
@ -778,17 +786,16 @@ where
)) ))
} }
pub fn client(&self) -> &B {
&self.client
}
#[maybe_async] #[maybe_async]
pub fn broadcast(&self, tx: Transaction) -> Result<Txid, Error> { pub fn broadcast(&self, tx: Transaction) -> Result<Txid, Error> {
maybe_await!(self.client.broadcast(&tx))?; maybe_await!(self.client.broadcast(&tx))?;
Ok(tx.txid()) Ok(tx.txid())
} }
#[maybe_async]
pub fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
Ok(maybe_await!(self.client.estimate_fee(target))?)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -7,7 +7,6 @@ use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorith
use super::utils::FeeRate; use super::utils::FeeRate;
use crate::types::UTXO; use crate::types::UTXO;
// TODO: add a flag to ignore change outputs (make them unspendable)
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TxBuilder<Cs: CoinSelectionAlgorithm> { pub struct TxBuilder<Cs: CoinSelectionAlgorithm> {
pub(crate) addressees: Vec<(Address, u64)>, pub(crate) addressees: Vec<(Address, u64)>,
@ -22,6 +21,7 @@ pub struct TxBuilder<Cs: CoinSelectionAlgorithm> {
pub(crate) rbf: Option<u32>, pub(crate) rbf: Option<u32>,
pub(crate) version: Version, pub(crate) version: Version,
pub(crate) change_policy: ChangeSpendPolicy, pub(crate) change_policy: ChangeSpendPolicy,
pub(crate) force_non_witness_utxo: bool,
pub(crate) coin_selection: Cs, pub(crate) coin_selection: Cs,
} }
@ -46,8 +46,8 @@ impl<Cs: CoinSelectionAlgorithm> TxBuilder<Cs> {
self self
} }
pub fn send_all(mut self, send_all: bool) -> Self { pub fn send_all(mut self) -> Self {
self.send_all = send_all; self.send_all = true;
self self
} }
@ -122,6 +122,16 @@ impl<Cs: CoinSelectionAlgorithm> TxBuilder<Cs> {
self self
} }
pub fn change_policy(mut self, change_policy: ChangeSpendPolicy) -> Self {
self.change_policy = change_policy;
self
}
pub fn force_non_witness_utxo(mut self) -> Self {
self.force_non_witness_utxo = true;
self
}
pub fn coin_selection<P: CoinSelectionAlgorithm>(self, coin_selection: P) -> TxBuilder<P> { pub fn coin_selection<P: CoinSelectionAlgorithm>(self, coin_selection: P) -> TxBuilder<P> {
TxBuilder { TxBuilder {
addressees: self.addressees, addressees: self.addressees,
@ -136,6 +146,7 @@ impl<Cs: CoinSelectionAlgorithm> TxBuilder<Cs> {
rbf: self.rbf, rbf: self.rbf,
version: self.version, version: self.version,
change_policy: self.change_policy, change_policy: self.change_policy,
force_non_witness_utxo: self.force_non_witness_utxo,
coin_selection, coin_selection,
} }
} }