diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 6a31deaa..676fd40c 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -326,9 +326,11 @@ interface TxBuilder { TxBuilder add_unspendable(OutPoint unspendable); + TxBuilder add_utxos(sequence outpoints); + TxBuilder add_utxo(OutPoint outpoint); - TxBuilder add_utxos(sequence outpoints); + TxBuilder add_foreign_utxo(OutPoint outpoint, Input psbt_input, u64 satisfaction_weight); TxBuilder do_not_spend_change(); diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs index c8549a0b..ac0e3478 100644 --- a/bdk-ffi/src/wallet.rs +++ b/bdk-ffi/src/wallet.rs @@ -249,6 +249,7 @@ pub(crate) struct TxBuilder { pub(crate) drain_to: Option, pub(crate) rbf: Option, pub(crate) data: Vec, + pub(crate) foreign_utxos: Vec<(OutPoint, Input, u64)> } impl TxBuilder { @@ -265,6 +266,7 @@ impl TxBuilder { drain_to: None, rbf: None, data: Vec::new(), + foreign_utxos: Vec::new(), } } @@ -318,6 +320,49 @@ impl TxBuilder { }) } + /// Add a foreign UTXO i.e. a UTXO not owned by this wallet. + /// At a minimum to add a foreign UTXO we need: + /// outpoint: To add it to the raw transaction. + /// psbt_input: To know the value. + /// satisfaction_weight: To know how much weight/vbytes the input will add to the transaction for fee calculation. + /// + /// There are several security concerns about adding foreign UTXOs that application developers should consider. + /// First, how do you know the value of the input is correct? If a non_witness_utxo is provided in the + /// psbt_input then this method implicitly verifies the value by checking it against the transaction. + /// If only a witness_utxo is provided then this method does not verify the value but just takes it as a + /// given – it is up to you to check that whoever sent you the input_psbt was not lying! + /// + /// Secondly, you must somehow provide satisfaction_weight of the input. Depending on your application + /// it may be important that this be known precisely. If not, a malicious counterparty may fool you into putting in + /// a value that is too low, giving the transaction a lower than expected feerate. They could also fool you + /// into putting a value that is too high causing you to pay a fee that is too high. The party who is broadcasting + /// the transaction can of course check the real input weight matches the expected weight prior to broadcasting. + /// + /// To guarantee the satisfaction_weight is correct, you can require the party providing the psbt_input provide + /// a miniscript descriptor for the input so you can check it against the script_pubkey and then ask it for the + /// max_satisfaction_weight. + /// + /// Errors + /// This method returns errors in the following circumstances: + /// The psbt_input does not contain a witness_utxo or non_witness_utxo. + /// The data in non_witness_utxo does not match what is in outpoint. + /// + /// Note unless you set only_witness_utxo any non-taproot psbt_input you pass to this method must + /// have non_witness_utxo set otherwise you will get an error when finish is called. + pub fn add_foreign_utxo( + &mut self, + outpoint: OutPoint, + psbt_input: Input, + satisfaction_weight: u64 + ) -> Arc { + let mut foreign_utxos = self.foreign_utxos.to_vec(); + foreign_utxos.append((outpoint, psbt_input, satisfaction_weight)); + Arc::new(TxBuilder { + foreign_utxos: foreign_utxos, + ..self.clone() + }) + } + /// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable. pub(crate) fn do_not_spend_change(&self) -> Arc { Arc::new(TxBuilder {