//! The provided output descriptors follow a custom LDK data format and are currently not fully
//! compatible with Bitcoin Core output descriptors.
-use bitcoin::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
+use bitcoin::amount::Amount;
+use bitcoin::bip32::{ChildNumber, Xpriv, Xpub};
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script::{Builder, Script, ScriptBuf};
use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
use bitcoin::ecdsa::Signature as EcdsaSignature;
-use bitcoin::network::constants::Network;
-use bitcoin::psbt::PartiallySignedTransaction;
+use bitcoin::network::Network;
use bitcoin::sighash;
use bitcoin::sighash::EcdsaSighashType;
+use bitcoin::transaction::Version;
-use bitcoin::bech32::u5;
-use bitcoin::hash_types::WPubkeyHash;
+use bech32::u5;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::schnorr;
#[cfg(taproot)]
use bitcoin::secp256k1::All;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
-use bitcoin::{secp256k1, Sequence, Txid, Witness};
+use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
+use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness};
use crate::chain::transaction::OutPoint;
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
+use crate::ln::chan_utils;
use crate::ln::chan_utils::{
- make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction,
- CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
+ get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
+ ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
+ HTLCOutputInCommitment, HolderCommitmentTransaction,
};
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::ln::channel_keys::{
- DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, RevocationBasepoint,
- RevocationKey,
+ add_public_key_tweak, DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey,
+ RevocationBasepoint, RevocationKey,
};
#[cfg(taproot)]
use crate::ln::msgs::PartialSignatureWithNonce;
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
use crate::ln::script::ShutdownScript;
-use crate::ln::{chan_utils, PaymentPreimage};
+use crate::ln::types::PaymentPreimage;
use crate::offers::invoice::UnsignedBolt12Invoice;
use crate::offers::invoice_request::UnsignedInvoiceRequest;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
use crate::crypto::chacha20::ChaCha20;
use crate::io::{self, Error};
use crate::ln::features::ChannelTypeFeatures;
-use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+use crate::ln::msgs::DecodeError;
use crate::prelude::*;
-use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::ecdsa::EcdsaChannelSigner;
#[cfg(taproot)]
use crate::sign::taproot::TaprootChannelSigner;
use crate::util::atomic_counter::AtomicCounter;
use crate::util::invoice::construct_invoice_preimage;
+use core::convert::TryInto;
use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(taproot)]
pub channel_keys_id: [u8; 32],
/// The value of the channel which this output originated from, possibly indirectly.
pub channel_value_satoshis: u64,
+ /// The channel public keys and other parameters needed to generate a spending transaction or
+ /// to provide to a re-derived signer through [`ChannelSigner::provide_channel_parameters`].
+ ///
+ /// Added as optional, but always `Some` if the descriptor was produced in v0.0.123 or later.
+ pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
}
+
impl DelayedPaymentOutputDescriptor {
/// The maximum length a well-formed witness spending one of these should have.
/// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
(8, revocation_pubkey, required),
(10, channel_keys_id, required),
(12, channel_value_satoshis, required),
+ (13, channel_transaction_parameters, option),
});
pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
/// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later.
pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
}
+
impl StaticPaymentOutputDescriptor {
/// Returns the `witness_script` of the spendable output.
///
impl SpendableOutputDescriptor {
/// Turns this into a [`bitcoin::psbt::Input`] which can be used to create a
- /// [`PartiallySignedTransaction`] which spends the given descriptor.
+ /// [`Psbt`] which spends the given descriptor.
///
/// Note that this does not include any signatures, just the information required to
/// construct the transaction and sign it.
///
/// This is not exported to bindings users as there is no standard serialization for an input.
/// See [`Self::create_spendable_outputs_psbt`] instead.
- pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
+ ///
+ /// The proprietary field is used to store add tweak for the signing key of this transaction.
+ /// See the [`DelayedPaymentBasepoint::derive_add_tweak`] docs for more info on add tweak and how to use it.
+ ///
+ /// To get the proprietary field use:
+ /// ```
+ /// use bitcoin::psbt::{Psbt};
+ /// use bitcoin::hashes::hex::FromHex;
+ ///
+ /// # let s = "70736274ff0100520200000001dee978529ab3e61a2987bea5183713d0e6d5ceb5ac81100fdb54a1a2\
+ /// # 69cef505000000000090000000011f26000000000000160014abb3ab63280d4ccc5c11d6b50fd427a8\
+ /// # e19d6470000000000001012b10270000000000002200200afe4736760d814a2651bae63b572d935d9a\
+ /// # b74a1a16c01774e341a32afa763601054d63210394a27a700617f5b7aee72bd4f8076b5770a582b7fb\
+ /// # d1d4ee2ea3802cd3cfbe2067029000b27521034629b1c8fdebfaeb58a74cd181f485e2c462e594cb30\
+ /// # 34dee655875f69f6c7c968ac20fc144c444b5f7370656e6461626c655f6f7574707574006164645f74\
+ /// # 7765616b20a86534f38ad61dc580ef41c3886204adf0911b81619c1ad7a2f5b5de39a2ba600000";
+ /// # let psbt = Psbt::deserialize(<Vec<u8> as FromHex>::from_hex(s).unwrap().as_slice()).unwrap();
+ /// let key = bitcoin::psbt::raw::ProprietaryKey {
+ /// prefix: "LDK_spendable_output".as_bytes().to_vec(),
+ /// subtype: 0,
+ /// key: "add_tweak".as_bytes().to_vec(),
+ /// };
+ /// let value = psbt
+ /// .inputs
+ /// .first()
+ /// .expect("Unable to get add tweak as there are no inputs")
+ /// .proprietary
+ /// .get(&key)
+ /// .map(|x| x.to_owned());
+ /// ```
+ pub fn to_psbt_input<T: secp256k1::Signing>(
+ &self, secp_ctx: &Secp256k1<T>,
+ ) -> bitcoin::psbt::Input {
match self {
SpendableOutputDescriptor::StaticOutput { output, .. } => {
// Is a standard P2WPKH, no need for witness script
bitcoin::psbt::Input { witness_utxo: Some(output.clone()), ..Default::default() }
},
- SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
- // TODO we could add the witness script as well
+ SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
+ channel_transaction_parameters,
+ per_commitment_point,
+ revocation_pubkey,
+ to_self_delay,
+ output,
+ ..
+ }) => {
+ let delayed_payment_basepoint = channel_transaction_parameters
+ .as_ref()
+ .map(|params| params.holder_pubkeys.delayed_payment_basepoint);
+
+ let (witness_script, add_tweak) =
+ if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
+ // Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
+ let add_tweak = basepoint.derive_add_tweak(&per_commitment_point);
+ let payment_key = DelayedPaymentKey(add_public_key_tweak(
+ secp_ctx,
+ &basepoint.to_public_key(),
+ &add_tweak,
+ ));
+
+ (
+ Some(get_revokeable_redeemscript(
+ &revocation_pubkey,
+ *to_self_delay,
+ &payment_key,
+ )),
+ Some(add_tweak),
+ )
+ } else {
+ (None, None)
+ };
+
bitcoin::psbt::Input {
- witness_utxo: Some(descriptor.output.clone()),
+ witness_utxo: Some(output.clone()),
+ witness_script,
+ proprietary: add_tweak
+ .map(|add_tweak| {
+ [(
+ bitcoin::psbt::raw::ProprietaryKey {
+ // A non standard namespace for spendable outputs, used to store the tweak needed
+ // to derive the private key
+ prefix: "LDK_spendable_output".as_bytes().to_vec(),
+ subtype: 0,
+ key: "add_tweak".as_bytes().to_vec(),
+ },
+ add_tweak.as_byte_array().to_vec(),
+ )]
+ .into_iter()
+ .collect()
+ })
+ .unwrap_or_default(),
..Default::default()
}
},
- SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
- // TODO we could add the witness script as well
- bitcoin::psbt::Input {
- witness_utxo: Some(descriptor.output.clone()),
- ..Default::default()
- }
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => bitcoin::psbt::Input {
+ witness_utxo: Some(descriptor.output.clone()),
+ witness_script: descriptor.witness_script(),
+ ..Default::default()
},
}
}
- /// Creates an unsigned [`PartiallySignedTransaction`] which spends the given descriptors to
+ /// Creates an unsigned [`Psbt`] which spends the given descriptors to
/// the given outputs, plus an output to the given change destination (if sufficient
/// change value remains). The PSBT will have a feerate, at least, of the given value.
///
/// does not match the one we can spend.
///
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
- pub fn create_spendable_outputs_psbt(
- descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+ pub fn create_spendable_outputs_psbt<T: secp256k1::Signing>(
+ secp_ctx: &Secp256k1<T>, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
locktime: Option<LockTime>,
- ) -> Result<(PartiallySignedTransaction, u64), ()> {
+ ) -> Result<(Psbt, u64), ()> {
let mut input = Vec::with_capacity(descriptors.len());
- let mut input_value = 0;
+ let mut input_value = Amount::ZERO;
let mut witness_weight = 0;
let mut output_set = hash_set_with_capacity(descriptors.len());
for outp in descriptors {
input_value += output.value;
},
}
- if input_value > MAX_VALUE_MSAT / 1000 {
+ if input_value > Amount::MAX_MONEY {
return Err(());
}
}
let mut tx = Transaction {
- version: 2,
+ version: Version::TWO,
lock_time: locktime.unwrap_or(LockTime::ZERO),
input,
output: outputs,
change_destination_script,
)?;
- let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
- let psbt = PartiallySignedTransaction {
+ let psbt_inputs =
+ descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx)).collect::<Vec<_>>();
+ let psbt = Psbt {
inputs: psbt_inputs,
outputs: vec![Default::default(); tx.output.len()],
unsigned_tx: tx,
&self, secp: &Secp256k1<C>,
) -> TxOut {
TxOut {
- script_pubkey: self.witness_script(secp).to_v0_p2wsh(),
- value: self.htlc.amount_msat / 1000,
+ script_pubkey: self.witness_script(secp).to_p2wsh(),
+ value: self.htlc.to_bitcoin_amount(),
}
}
}
/// Derives the channel signer required to sign the HTLC input.
- pub fn derive_channel_signer<S: WriteableEcdsaChannelSigner, SP: Deref>(
- &self, signer_provider: &SP,
- ) -> S
+ pub fn derive_channel_signer<S: EcdsaChannelSigner, SP: Deref>(&self, signer_provider: &SP) -> S
where
SP::Target: SignerProvider<EcdsaSigner = S>,
{
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()>;
}
+/// A trait that describes a wallet capable of creating a spending [`Transaction`] from a set of
+/// [`SpendableOutputDescriptor`]s.
+pub trait OutputSpender {
+ /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
+ /// output to the given change destination (if sufficient change value remains). The
+ /// transaction will have a feerate, at least, of the given value.
+ ///
+ /// The `locktime` argument is used to set the transaction's locktime. If `None`, the
+ /// transaction will have a locktime of 0. It it recommended to set this to the current block
+ /// height to avoid fee sniping, unless you have some specific reason to use a different
+ /// locktime.
+ ///
+ /// Returns `Err(())` if the output value is greater than the input value minus required fee,
+ /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
+ /// does not match the one we can spend.
+ fn spend_spendable_outputs<C: Signing>(
+ &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+ change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+ locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+ ) -> Result<Transaction, ()>;
+}
+
// Primarily needed in doctests because of https://github.com/rust-lang/rust/issues/67295
/// A dynamic [`SignerProvider`] temporarily needed for doc tests.
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
#[cfg(taproot)]
#[doc(hidden)]
#[deprecated(note = "Remove once taproot cfg is removed")]
dyn SignerProvider<EcdsaSigner = InMemorySigner, TaprootSigner = InMemorySigner>;
/// A dynamic [`SignerProvider`] temporarily needed for doc tests.
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
#[cfg(not(taproot))]
#[doc(hidden)]
#[deprecated(note = "Remove once taproot cfg is removed")]
/// A trait that can return signer instances for individual channels.
pub trait SignerProvider {
- /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
- type EcdsaSigner: WriteableEcdsaChannelSigner;
+ /// A type which implements [`EcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
+ type EcdsaSigner: EcdsaChannelSigner;
#[cfg(taproot)]
/// A type which implements [`TaprootChannelSigner`]
type TaprootSigner: TaprootChannelSigner;
/// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream.
/// This is only called during deserialization of other objects which contain
- /// [`WriteableEcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
+ /// [`EcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
/// The bytes are exactly those which `<Self::Signer as Writeable>::write()` writes, and
/// contain no versioning scheme. You may wish to include your own version prefix and ensure
/// you've read all of the provided bytes to ensure no corruption occurred.
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()>;
}
-/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
+/// A helper trait that describes an on-chain wallet capable of returning a (change) destination
+/// script.
+pub trait ChangeDestinationSource {
+ /// Returns a script pubkey which can be used as a change destination for
+ /// [`OutputSpender::spend_spendable_outputs`].
+ ///
+ /// This method should return a different value each time it is called, to avoid linking
+ /// on-chain funds controlled to the same user.
+ fn get_change_destination_script(&self) -> Result<ScriptBuf, ()>;
+}
+
+/// A simple implementation of [`EcdsaChannelSigner`] that just keeps the private keys in memory.
///
/// This implementation performs no policy checks and is insufficient by itself as
/// a secure external signer.
};
let sighash = hash_to_message!(
&sighash::SighashCache::new(spend_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
input_idx,
&witness_script,
descriptor.output.value,
);
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
let payment_script = if supports_anchors_zero_fee_htlc_tx {
- witness_script.to_v0_p2wsh()
+ witness_script.to_p2wsh()
} else {
- ScriptBuf::new_v0_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
+ ScriptBuf::new_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
};
if payment_script != descriptor.output.script_pubkey {
);
let sighash = hash_to_message!(
&sighash::SighashCache::new(spend_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
input_idx,
&witness_script,
descriptor.output.value,
};
let htlc_sighash = hash_to_message!(
&sighash::SighashCache::new(&htlc_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
0,
&htlc_redeemscript,
- htlc.amount_msat / 1000,
+ htlc.to_bitcoin_amount(),
htlc_sighashtype
)
.unwrap()[..]
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
let sighash = hash_to_message!(
&sighash_parts
- .segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All)
+ .p2wsh_signature_hash(
+ input,
+ &witness_script,
+ Amount::from_sat(amount),
+ EcdsaSighashType::All
+ )
.unwrap()[..]
);
return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self));
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
let sighash = hash_to_message!(
&sighash_parts
- .segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All)
+ .p2wsh_signature_hash(
+ input,
+ &witness_script,
+ Amount::from_sat(amount),
+ EcdsaSighashType::All
+ )
.unwrap()[..]
);
return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self));
) -> Result<Signature, ()> {
let witness_script = htlc_descriptor.witness_script(secp_ctx);
let sighash = &sighash::SighashCache::new(&*htlc_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
input,
&witness_script,
- htlc_descriptor.htlc.amount_msat / 1000,
+ htlc_descriptor.htlc.to_bitcoin_amount(),
EcdsaSighashType::All,
)
.map_err(|_| ())?;
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
let sighash = hash_to_message!(
&sighash_parts
- .segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All)
+ .p2wsh_signature_hash(
+ input,
+ &witness_script,
+ Amount::from_sat(amount),
+ EcdsaSighashType::All
+ )
.unwrap()[..]
);
Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
let witness_script =
chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey);
let sighash = sighash::SighashCache::new(&*anchor_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
input,
&witness_script,
- ANCHOR_OUTPUT_VALUE_SATOSHI,
+ Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
EcdsaSighashType::All,
)
.unwrap();
const MIN_SERIALIZATION_VERSION: u8 = 1;
-impl WriteableEcdsaChannelSigner for InMemorySigner {}
-
impl Writeable for InMemorySigner {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
inbound_payment_key: KeyMaterial,
destination_script: ScriptBuf,
shutdown_pubkey: PublicKey,
- channel_master_key: ExtendedPrivKey,
+ channel_master_key: Xpriv,
channel_child_index: AtomicUsize,
entropy_source: RandomBytes,
pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
let secp_ctx = Secp256k1::new();
// Note that when we aren't serializing the key, network doesn't matter
- match ExtendedPrivKey::new_master(Network::Testnet, seed) {
+ match Xpriv::new_master(Network::Testnet, seed) {
Ok(master_key) => {
let node_secret = master_key
- .ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap())
+ .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(0).unwrap())
.expect("Your RNG is busted")
.private_key;
let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret);
let destination_script = match master_key
- .ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1).unwrap())
+ .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(1).unwrap())
{
Ok(destination_key) => {
let wpubkey_hash = WPubkeyHash::hash(
- &ExtendedPubKey::from_priv(&secp_ctx, &destination_key)
- .to_pub()
- .to_bytes(),
+ &Xpub::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes(),
);
Builder::new()
.push_opcode(opcodes::all::OP_PUSHBYTES_0)
Err(_) => panic!("Your RNG is busted"),
};
let shutdown_pubkey = match master_key
- .ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2).unwrap())
+ .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(2).unwrap())
{
- Ok(shutdown_key) => {
- ExtendedPubKey::from_priv(&secp_ctx, &shutdown_key).public_key
- },
+ Ok(shutdown_key) => Xpub::from_priv(&secp_ctx, &shutdown_key).public_key,
Err(_) => panic!("Your RNG is busted"),
};
let channel_master_key = master_key
- .ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap())
+ .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(3).unwrap())
.expect("Your RNG is busted");
let inbound_payment_key: SecretKey = master_key
- .ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap())
+ .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(5).unwrap())
.expect("Your RNG is busted")
.private_key;
let mut inbound_pmt_key_bytes = [0; 32];
self.node_secret
}
- /// Derive an old [`WriteableEcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
+ /// Derive an old [`EcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
pub fn derive_channel_keys(
&self, channel_value_satoshis: u64, params: &[u8; 32],
) -> InMemorySigner {
// starting_time provided in the constructor) to be unique.
let child_privkey = self
.channel_master_key
- .ckd_priv(
+ .derive_priv(
&self.secp_ctx,
- ChildNumber::from_hardened_idx((chan_id as u32) % (1 << 31))
+ &ChildNumber::from_hardened_idx((chan_id as u32) % (1 << 31))
.expect("key space exhausted"),
)
.expect("Your RNG is busted");
)
}
- /// Signs the given [`PartiallySignedTransaction`] which spends the given [`SpendableOutputDescriptor`]s.
+ /// Signs the given [`Psbt`] which spends the given [`SpendableOutputDescriptor`]s.
/// The resulting inputs will be finalized and the PSBT will be ready for broadcast if there
/// are no other inputs that need signing.
///
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
pub fn sign_spendable_outputs_psbt<C: Signing>(
- &self, descriptors: &[&SpendableOutputDescriptor], mut psbt: PartiallySignedTransaction,
- secp_ctx: &Secp256k1<C>,
- ) -> Result<PartiallySignedTransaction, ()> {
+ &self, descriptors: &[&SpendableOutputDescriptor], mut psbt: Psbt, secp_ctx: &Secp256k1<C>,
+ ) -> Result<Psbt, ()> {
let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
for outp in descriptors {
let get_input_idx = |outpoint: &OutPoint| {
if output.script_pubkey == self.destination_script { 1 } else { 2 };
let secret = {
// Note that when we aren't serializing the key, network doesn't matter
- match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
+ match Xpriv::new_master(Network::Testnet, &self.seed) {
Ok(master_key) => {
- match master_key.ckd_priv(
+ match master_key.derive_priv(
&secp_ctx,
- ChildNumber::from_hardened_idx(derivation_idx)
+ &ChildNumber::from_hardened_idx(derivation_idx)
.expect("key space exhausted"),
) {
Ok(key) => key,
Err(_) => panic!("Your rng is busted"),
}
};
- let pubkey = ExtendedPubKey::from_priv(&secp_ctx, &secret).to_pub();
+ let pubkey = Xpub::from_priv(&secp_ctx, &secret).to_pub();
if derivation_idx == 2 {
assert_eq!(pubkey.inner, self.shutdown_pubkey);
}
let sighash = hash_to_message!(
&sighash::SighashCache::new(&psbt.unsigned_tx)
- .segwit_signature_hash(
+ .p2wsh_signature_hash(
input_idx,
&witness_script,
output.value,
Ok(psbt)
}
-
- /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
- /// output to the given change destination (if sufficient change value remains). The
- /// transaction will have a feerate, at least, of the given value.
- ///
- /// The `locktime` argument is used to set the transaction's locktime. If `None`, the
- /// transaction will have a locktime of 0. It it recommended to set this to the current block
- /// height to avoid fee sniping, unless you have some specific reason to use a different
- /// locktime.
- ///
- /// Returns `Err(())` if the output value is greater than the input value minus required fee,
- /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
- /// does not match the one we can spend.
- ///
- /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
- ///
- /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
- /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
- pub fn spend_spendable_outputs<C: Signing>(
- &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
- change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
- locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
- ) -> Result<Transaction, ()> {
- let (mut psbt, expected_max_weight) =
- SpendableOutputDescriptor::create_spendable_outputs_psbt(
- descriptors,
- outputs,
- change_destination_script,
- feerate_sat_per_1000_weight,
- locktime,
- )?;
- psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;
-
- let spend_tx = psbt.extract_tx();
-
- debug_assert!(expected_max_weight >= spend_tx.weight().to_wu());
- // Note that witnesses with a signature vary somewhat in size, so allow
- // `expected_max_weight` to overshoot by up to 3 bytes per input.
- debug_assert!(
- expected_max_weight <= spend_tx.weight().to_wu() + descriptors.len() as u64 * 3
- );
-
- Ok(spend_tx)
- }
}
impl EntropySource for KeysManager {
&self, invoice_request: &UnsignedInvoiceRequest,
) -> Result<schnorr::Signature, ()> {
let message = invoice_request.tagged_hash().as_digest();
- let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
+ let keys = Keypair::from_secret_key(&self.secp_ctx, &self.node_secret);
let aux_rand = self.get_secure_random_bytes();
Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
}
&self, invoice: &UnsignedBolt12Invoice,
) -> Result<schnorr::Signature, ()> {
let message = invoice.tagged_hash().as_digest();
- let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
+ let keys = Keypair::from_secret_key(&self.secp_ctx, &self.node_secret);
let aux_rand = self.get_secure_random_bytes();
Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
}
}
}
+impl OutputSpender for KeysManager {
+ /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
+ /// output to the given change destination (if sufficient change value remains).
+ ///
+ /// See [`OutputSpender::spend_spendable_outputs`] documentation for more information.
+ ///
+ /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
+ ///
+ /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
+ /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
+ fn spend_spendable_outputs<C: Signing>(
+ &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+ change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+ locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+ ) -> Result<Transaction, ()> {
+ let (mut psbt, expected_max_weight) =
+ SpendableOutputDescriptor::create_spendable_outputs_psbt(
+ secp_ctx,
+ descriptors,
+ outputs,
+ change_destination_script,
+ feerate_sat_per_1000_weight,
+ locktime,
+ )?;
+ psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;
+
+ let spend_tx = psbt.extract_tx_unchecked_fee_rate();
+
+ debug_assert!(expected_max_weight >= spend_tx.weight().to_wu());
+ // Note that witnesses with a signature vary somewhat in size, so allow
+ // `expected_max_weight` to overshoot by up to 3 bytes per input.
+ debug_assert!(
+ expected_max_weight <= spend_tx.weight().to_wu() + descriptors.len() as u64 * 3
+ );
+
+ Ok(spend_tx)
+ }
+}
+
impl SignerProvider for KeysManager {
type EcdsaSigner = InMemorySigner;
#[cfg(taproot)]
}
}
+impl OutputSpender for PhantomKeysManager {
+ /// See [`OutputSpender::spend_spendable_outputs`] and [`KeysManager::spend_spendable_outputs`]
+ /// for documentation on this method.
+ fn spend_spendable_outputs<C: Signing>(
+ &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+ change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+ locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+ ) -> Result<Transaction, ()> {
+ self.inner.spend_spendable_outputs(
+ descriptors,
+ outputs,
+ change_destination_script,
+ feerate_sat_per_1000_weight,
+ locktime,
+ secp_ctx,
+ )
+ }
+}
+
impl SignerProvider for PhantomKeysManager {
type EcdsaSigner = InMemorySigner;
#[cfg(taproot)]
}
}
- /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
- pub fn spend_spendable_outputs<C: Signing>(
- &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
- change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
- locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
- ) -> Result<Transaction, ()> {
- self.inner.spend_spendable_outputs(
- descriptors,
- outputs,
- change_destination_script,
- feerate_sat_per_1000_weight,
- locktime,
- secp_ctx,
- )
- }
-
/// See [`KeysManager::derive_channel_keys`] for documentation on this method.
pub fn derive_channel_keys(
&self, channel_value_satoshis: u64, params: &[u8; 32],