use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
use lightning::ln::script::ShutdownScript;
use lightning::ln::functional_test_utils::*;
+use lightning::offers::invoice::UnsignedBolt12Invoice;
+use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::errors::APIError;
use lightning::util::logger::Logger;
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
+use bitcoin::secp256k1::schnorr;
use std::mem;
use std::cmp::{self, Ordering};
unreachable!()
}
+ fn sign_bolt12_invoice_request(
+ &self, _invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
+ fn sign_bolt12_invoice(
+ &self, _invoice: &UnsignedBolt12Invoice,
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
let secp_ctx = Secp256k1::signing_only();
use lightning::ln::msgs::{self, DecodeError};
use lightning::ln::script::ShutdownScript;
use lightning::ln::functional_test_utils::*;
+use lightning::offers::invoice::UnsignedBolt12Invoice;
+use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
use lightning::routing::utxo::UtxoLookup;
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
+use bitcoin::secp256k1::schnorr;
use std::cell::RefCell;
use hashbrown::{HashMap, hash_map};
unreachable!()
}
+ fn sign_bolt12_invoice_request(
+ &self, _invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
+ fn sign_bolt12_invoice(
+ &self, _invoice: &UnsignedBolt12Invoice,
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
let secp_ctx = Secp256k1::signing_only();
if signing_pubkey == odd_pubkey || signing_pubkey == even_pubkey {
unsigned_invoice
.sign::<_, Infallible>(
- |digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap()
.write(&mut buffer)
} else {
unsigned_invoice
.sign::<_, Infallible>(
- |digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap_err();
}
SecretKey::from_slice(&[byte; 32]).unwrap()
}
-fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
- invoice_request: &'a InvoiceRequest, secp_ctx: &Secp256k1<T>
-) -> Result<UnsignedBolt12Invoice<'a>, Bolt12SemanticError> {
+fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
+ invoice_request: &InvoiceRequest, secp_ctx: &Secp256k1<T>
+) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
let entropy_source = Randomness {};
let paths = vec![
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
if let Ok(invoice_request) = build_response(&offer, pubkey) {
invoice_request
.sign::<_, Infallible>(
- |digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap()
.write(&mut buffer)
}
}
-fn build_response<'a>(
- offer: &'a Offer, pubkey: PublicKey
-) -> Result<UnsignedInvoiceRequest<'a>, Bolt12SemanticError> {
+fn build_response(
+ offer: &Offer, pubkey: PublicKey
+) -> Result<UnsignedInvoiceRequest, Bolt12SemanticError> {
let mut builder = offer.request_invoice(vec![42; 64], pubkey)?;
builder = match offer.amount() {
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
+use bitcoin::secp256k1::schnorr;
use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
use lightning::ln::script::ShutdownScript;
+use lightning::offers::invoice::UnsignedBolt12Invoice;
+use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::util::enforcing_trait_impls::EnforcingSigner;
use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable, Writer};
unreachable!()
}
+ fn sign_bolt12_invoice_request(
+ &self, _invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
+ fn sign_bolt12_invoice(
+ &self, _invoice: &UnsignedBolt12Invoice,
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
fn sign_gossip_message(&self, _msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<bitcoin::secp256k1::ecdsa::Signature, ()> {
unreachable!()
}
if let Ok(invoice) = build_response(&refund, pubkey, &secp_ctx) {
invoice
.sign::<_, Infallible>(
- |digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap()
.write(&mut buffer)
SecretKey::from_slice(&[byte; 32]).unwrap()
}
-fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
- refund: &'a Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
-) -> Result<UnsignedBolt12Invoice<'a>, Bolt12SemanticError> {
+fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
+ refund: &Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
+) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
let entropy_source = Randomness {};
let paths = vec![
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
};
log_trace!(logger, "Creating phantom invoice from {} participating nodes with payment hash {}",
- phantom_route_hints.len(), log_bytes!(payment_hash.0));
+ phantom_route_hints.len(), &payment_hash);
let mut invoice = invoice
.duration_since_epoch(duration_since_epoch)
return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
}
- log_trace!(logger, "Creating invoice with payment hash {}", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Creating invoice with payment hash {}", &payment_hash);
let invoice = match description {
Bolt11InvoiceDescription::Direct(description) => {
},
};
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction {}, waiting for confirmation (at height {})",
- log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type,
+ &htlc.payment_hash, $commitment_tx, $commitment_tx_type,
$commitment_txid_confirmed, entry.confirmation_threshold());
$self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
log_debug!(logger, "HTLC {} failure update in {} has got enough confirmations to be passed upstream",
- log_bytes!(payment_hash.0), entry.txid);
+ &payment_hash, entry.txid);
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
payment_hash,
payment_preimage: None,
OnchainEvent::MaturingOutput { descriptor } => {
log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
self.pending_events.push(Event::SpendableOutputs {
- outputs: vec![descriptor]
+ outputs: vec![descriptor],
+ channel_id: Some(self.funding_info.0.to_channel_id()),
});
self.spendable_txids_confirmed.push(entry.txid);
},
(outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) {
log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!",
$tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(),
- if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0),
+ if outbound_htlc { "outbound" } else { "inbound" }, &$htlc.payment_hash,
if revocation_sig_claim { "revocation sig" } else { "preimage claim after we'd passed the HTLC resolution back. We can likely claim the HTLC output with a revocation claim" });
} else {
log_info!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}",
$tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(),
- if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0),
+ if outbound_htlc { "outbound" } else { "inbound" }, &$htlc.payment_hash,
if revocation_sig_claim { "revocation sig" } else if accepted_preimage_claim || offered_preimage_claim { "preimage" } else { "timeout" });
}
}
commitment_tx_output_idx: Some(input.previous_output.vout),
},
};
- log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
+ log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", &payment_hash, entry.confirmation_threshold());
self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
SpendableOutputs {
/// The outputs which you should store as spendable by you.
outputs: Vec<SpendableOutputDescriptor>,
+ /// The `channel_id` indicating which channel the spendable outputs belong to.
+ ///
+ /// This will always be `Some` for events generated by LDK versions 0.0.117 and above.
+ channel_id: Option<[u8; 32]>,
},
/// This event is generated when a payment has been successfully forwarded through us and a
/// forwarding fee earned.
// Note that we now ignore these on the read end as we'll re-generate them in
// ChannelManager, we write them here only for backwards compatibility.
},
- &Event::SpendableOutputs { ref outputs } => {
+ &Event::SpendableOutputs { ref outputs, channel_id } => {
5u8.write(writer)?;
write_tlv_fields!(writer, {
(0, WithoutLength(outputs), required),
+ (1, channel_id, option),
});
},
&Event::HTLCIntercepted { requested_next_hop_scid, payment_hash, inbound_amount_msat, expected_outbound_amount_msat, intercept_id } => {
5u8 => {
let f = || {
let mut outputs = WithoutLength(Vec::new());
+ let mut channel_id: Option<[u8; 32]> = None;
read_tlv_fields!(reader, {
(0, outputs, required),
+ (1, channel_id, option),
});
- Ok(Some(Event::SpendableOutputs { outputs: outputs.0 }))
+ Ok(Some(Event::SpendableOutputs { outputs: outputs.0, channel_id }))
};
f()
},
use crate::chain::transaction::OutPoint;
use crate::chain::{ChannelMonitorUpdateStatus, Listen, Watch};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason, HTLCDestination};
-use crate::ln::channelmanager::{ChannelManager, RAACommitmentOrder, PaymentSendFailure, PaymentId, RecipientOnionFields};
+use crate::ln::channelmanager::{RAACommitmentOrder, PaymentSendFailure, PaymentId, RecipientOnionFields};
use crate::ln::channel::AnnouncementSigsState;
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler};
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator};
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS, CLOSED_CHANNEL_UPDATE_ID};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::sign::{WriteableEcdsaChannelSigner, EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
+use crate::sign::{EcdsaChannelSigner, WriteableEcdsaChannelSigner, EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
use crate::events::ClosureReason;
use crate::routing::gossip::NodeId;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
#[cfg(any(test, fuzzing, debug_assertions))]
use crate::sync::Mutex;
use bitcoin::hashes::hex::ToHex;
+use crate::sign::type_resolver::ChannelSignerType;
#[cfg(test)]
pub struct ChannelValueStat {
}
/// Contains everything about the channel including state, and various flags.
-pub(super) struct ChannelContext<Signer: ChannelSigner> {
+pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
config: LegacyChannelConfig,
// Track the previous `ChannelConfig` so that we can continue forwarding HTLCs that were
latest_monitor_update_id: u64,
- holder_signer: Signer,
+ holder_signer: ChannelSignerType<<SP::Target as SignerProvider>::Signer>,
shutdown_scriptpubkey: Option<ShutdownScript>,
destination_script: Script,
blocked_monitor_updates: Vec<PendingChannelMonitorUpdate>,
}
-impl<Signer: ChannelSigner> ChannelContext<Signer> {
+impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
/// Allowed in any state (including after shutdown)
pub fn get_update_time_counter(&self) -> u32 {
self.update_time_counter
feerate_per_kw as u64 * htlc_timeout_tx_weight(self.get_channel_type()) / 1000
};
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
- log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
+ log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, &$htlc.payment_hash, $htlc.amount_msat);
included_non_dust_htlcs.push((htlc_in_tx, $source));
} else {
- log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {} due to dust limit", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
+ log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {} due to dust limit", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, &$htlc.payment_hash, $htlc.amount_msat);
included_dust_htlcs.push((htlc_in_tx, $source));
}
} else {
feerate_per_kw as u64 * htlc_success_tx_weight(self.get_channel_type()) / 1000
};
if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
- log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
+ log_trace!(logger, " ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, &$htlc.payment_hash, $htlc.amount_msat);
included_non_dust_htlcs.push((htlc_in_tx, $source));
} else {
- log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
+ log_trace!(logger, " ...including {} {} dust HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, &$htlc.payment_hash, $htlc.amount_msat);
included_dust_htlcs.push((htlc_in_tx, $source));
}
}
add_htlc_output!(htlc, false, None, state_name);
remote_htlc_total_msat += htlc.amount_msat;
} else {
- log_trace!(logger, " ...not including inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name);
+ log_trace!(logger, " ...not including inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, &htlc.payment_hash, htlc.amount_msat, state_name);
match &htlc.state {
&InboundHTLCState::LocalRemoved(ref reason) => {
if generated_by_local {
add_htlc_output!(htlc, true, Some(&htlc.source), state_name);
local_htlc_total_msat += htlc.amount_msat;
} else {
- log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name);
+ log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, &htlc.payment_hash, htlc.amount_msat, state_name);
match htlc.state {
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_))|OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => {
value_to_self_msat_offset -= htlc.amount_msat as i64;
/// The result is a transaction which we can revoke broadcastership of (ie a "local" transaction)
/// TODO Some magic rust shit to compile-time check this?
fn build_holder_transaction_keys(&self, commitment_number: u64) -> TxCreationKeys {
- let per_commitment_point = self.holder_signer.get_per_commitment_point(commitment_number, &self.secp_ctx);
+ let per_commitment_point = self.holder_signer.as_ref().get_per_commitment_point(commitment_number, &self.secp_ctx);
let delayed_payment_base = &self.get_holder_pubkeys().delayed_payment_basepoint;
let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint;
let counterparty_pubkeys = self.get_counterparty_pubkeys();
//
// Holder designates channel data owned for the benefit of the user client.
// Counterparty designates channel data owned by the another channel participant entity.
-pub(super) struct Channel<Signer: ChannelSigner> {
- pub context: ChannelContext<Signer>,
+pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
+ pub context: ChannelContext<SP>,
}
#[cfg(any(test, fuzzing))]
feerate: u32,
}
-impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
+impl<SP: Deref> Channel<SP> where
+ SP::Target: SignerProvider,
+ <SP::Target as SignerProvider>::Signer: WriteableEcdsaChannelSigner
+{
fn check_remote_fee<F: Deref, L: Deref>(
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,
feerate_per_kw: u32, cur_feerate_per_kw: Option<u32>, logger: &L
InboundHTLCState::LocalRemoved(ref reason) => {
if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
} else {
- log_warn!(logger, "Have preimage and want to fulfill HTLC with payment hash {} we already failed against channel {}", log_bytes!(htlc.payment_hash.0), log_bytes!(self.context.channel_id()));
+ log_warn!(logger, "Have preimage and want to fulfill HTLC with payment hash {} we already failed against channel {}", &htlc.payment_hash, log_bytes!(self.context.channel_id()));
debug_assert!(false, "Tried to fulfill an HTLC that was already failed");
}
return UpdateFulfillFetch::DuplicateClaim {};
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
return UpdateFulfillFetch::NewClaim { monitor_update, htlc_value_msat, msg: None };
}
- log_trace!(logger, "Upgrading HTLC {} to LocalRemoved with a Fulfill in channel {}!", log_bytes!(htlc.payment_hash.0), log_bytes!(self.context.channel_id));
+ log_trace!(logger, "Upgrading HTLC {} to LocalRemoved with a Fulfill in channel {}!", &htlc.payment_hash, log_bytes!(self.context.channel_id));
htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(payment_preimage_arg.clone()));
}
/// Handles a funding_signed message from the remote end.
/// If this call is successful, broadcast the funding transaction (and not before!)
- pub fn funding_signed<SP: Deref, L: Deref>(
+ pub fn funding_signed<L: Deref>(
&mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
- ) -> Result<ChannelMonitor<Signer>, ChannelError>
+ ) -> Result<ChannelMonitor<<SP::Target as SignerProvider>::Signer>, ChannelError>
where
- SP::Target: SignerProvider<Signer = Signer>,
L::Target: Logger
{
if !self.context.is_outbound() {
self.context.counterparty_funding_pubkey()
);
- self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
+ self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
self.context.counterparty_funding_pubkey()
);
- self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, commitment_stats.preimages)
+ self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.preimages)
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
// Update state now that we've passed all the can-fail calls...
} else { None };
if let Some(forward_info) = new_forward {
log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToAnnounce due to commitment_signed in channel {}.",
- log_bytes!(htlc.payment_hash.0), log_bytes!(self.context.channel_id));
+ &htlc.payment_hash, log_bytes!(self.context.channel_id));
htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info);
need_commitment = true;
}
for htlc in self.context.pending_outbound_htlcs.iter_mut() {
if let &mut OutboundHTLCState::RemoteRemoved(ref mut outcome) = &mut htlc.state {
log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToRemove due to commitment_signed in channel {}.",
- log_bytes!(htlc.payment_hash.0), log_bytes!(self.context.channel_id));
+ &htlc.payment_hash, log_bytes!(self.context.channel_id));
// Grab the preimage, if it exists, instead of cloning
let mut reason = OutboundHTLCOutcome::Success(None);
mem::swap(outcome, &mut reason);
match e {
ChannelError::Ignore(ref msg) => {
log_info!(logger, "Failed to send HTLC with payment_hash {} due to {} in channel {}",
- log_bytes!(payment_hash.0), msg, log_bytes!(self.context.channel_id()));
+ &payment_hash, msg, log_bytes!(self.context.channel_id()));
// If we fail to send here, then this HTLC should
// be failed backwards. Failing to send here
// indicates that this HTLC may keep being put back
*self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
}
- self.context.holder_signer.validate_counterparty_revocation(
- self.context.cur_counterparty_commitment_transaction_number + 1,
- &secret
- ).map_err(|_| ChannelError::Close("Failed to validate revocation from peer".to_owned()))?;
+ match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ ecdsa.validate_counterparty_revocation(
+ self.context.cur_counterparty_commitment_transaction_number + 1,
+ &secret
+ ).map_err(|_| ChannelError::Close("Failed to validate revocation from peer".to_owned()))?;
+ }
+ };
self.context.commitment_secrets.provide_secret(self.context.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret)
.map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?;
// We really shouldnt have two passes here, but retain gives a non-mutable ref (Rust bug)
pending_inbound_htlcs.retain(|htlc| {
if let &InboundHTLCState::LocalRemoved(ref reason) = &htlc.state {
- log_trace!(logger, " ...removing inbound LocalRemoved {}", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...removing inbound LocalRemoved {}", &htlc.payment_hash);
if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
value_to_self_msat_diff += htlc.amount_msat as i64;
}
});
pending_outbound_htlcs.retain(|htlc| {
if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) = &htlc.state {
- log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", &htlc.payment_hash);
if let OutboundHTLCOutcome::Failure(reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
mem::swap(&mut state, &mut htlc.state);
if let InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info) = state {
- log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce {} to AwaitingAnnouncedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce {} to AwaitingAnnouncedRemoteRevoke", &htlc.payment_hash);
htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info);
require_commitment = true;
} else if let InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info) = state {
match forward_info {
PendingHTLCStatus::Fail(fail_msg) => {
- log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to LocalRemoved due to PendingHTLCStatus indicating failure", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to LocalRemoved due to PendingHTLCStatus indicating failure", &htlc.payment_hash);
require_commitment = true;
match fail_msg {
HTLCFailureMsg::Relay(msg) => {
}
},
PendingHTLCStatus::Forward(forward_info) => {
- log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to Committed", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to Committed", &htlc.payment_hash);
to_forward_infos.push((forward_info, htlc.htlc_id));
htlc.state = InboundHTLCState::Committed;
}
}
for htlc in pending_outbound_htlcs.iter_mut() {
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
- log_trace!(logger, " ...promoting outbound LocalAnnounced {} to Committed", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting outbound LocalAnnounced {} to Committed", &htlc.payment_hash);
htlc.state = OutboundHTLCState::Committed;
}
if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
- log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", &htlc.payment_hash);
// Grab the preimage, if it exists, instead of cloning
let mut reason = OutboundHTLCOutcome::Success(None);
mem::swap(outcome, &mut reason);
assert!(!self.context.is_outbound() || self.context.minimum_depth == Some(0),
"Funding transaction broadcast by the local client before it should have - LDK didn't do it!");
self.context.monitor_pending_channel_ready = false;
- let next_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let next_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
Some(msgs::ChannelReady {
channel_id: self.context.channel_id(),
next_per_commitment_point,
if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish".to_owned()));
}
- Channel::<Signer>::check_remote_fee(&self.context.channel_type, fee_estimator, msg.feerate_per_kw, Some(self.context.feerate_per_kw), logger)?;
+ Channel::<SP>::check_remote_fee(&self.context.channel_type, fee_estimator, msg.feerate_per_kw, Some(self.context.feerate_per_kw), logger)?;
let feerate_over_dust_buffer = msg.feerate_per_kw > self.context.get_dust_buffer_feerate(None);
self.context.pending_update_fee = Some((msg.feerate_per_kw, FeeUpdateState::RemoteAnnounced));
}
fn get_last_revoke_and_ack(&self) -> msgs::RevokeAndACK {
- let next_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
- let per_commitment_secret = self.context.holder_signer.release_commitment_secret(self.context.cur_holder_commitment_transaction_number + 2);
+ let next_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let per_commitment_secret = self.context.holder_signer.as_ref().release_commitment_secret(self.context.cur_holder_commitment_transaction_number + 2);
msgs::RevokeAndACK {
channel_id: self.context.channel_id,
per_commitment_secret,
}
if msg.next_remote_commitment_number > 0 {
- let expected_point = self.context.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.context.secp_ctx);
+ let expected_point = self.context.holder_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.context.secp_ctx);
let given_secret = SecretKey::from_slice(&msg.your_last_per_commitment_secret)
.map_err(|_| ChannelError::Close("Peer sent a garbage channel_reestablish with unparseable secret key".to_owned()))?;
if expected_point != PublicKey::from_secret_key(&self.context.secp_ctx, &given_secret) {
}
// We have OurChannelReady set!
- let next_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let next_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
return Ok(ReestablishResponses {
channel_ready: Some(msgs::ChannelReady {
channel_id: self.context.channel_id(),
let channel_ready = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.context.cur_holder_commitment_transaction_number == 1 {
// We should never have to worry about MonitorUpdateInProgress resending ChannelReady
- let next_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let next_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
Some(msgs::ChannelReady {
channel_id: self.context.channel_id(),
next_per_commitment_point,
log_trace!(logger, "Proposing initial closing_signed for our counterparty with a fee range of {}-{} sat (with initial proposal {} sats)",
our_min_fee, our_max_fee, total_fee_satoshis);
- let sig = self.context.holder_signer
- .sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
- .map_err(|()| ChannelError::Close("Failed to get signature for closing transaction.".to_owned()))?;
+ match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let sig = ecdsa
+ .sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
+ .map_err(|()| ChannelError::Close("Failed to get signature for closing transaction.".to_owned()))?;
- self.context.last_sent_closing_fee = Some((total_fee_satoshis, sig.clone()));
- Ok((Some(msgs::ClosingSigned {
- channel_id: self.context.channel_id,
- fee_satoshis: total_fee_satoshis,
- signature: sig,
- fee_range: Some(msgs::ClosingSignedFeeRange {
- min_fee_satoshis: our_min_fee,
- max_fee_satoshis: our_max_fee,
- }),
- }), None))
+ self.context.last_sent_closing_fee = Some((total_fee_satoshis, sig.clone()));
+ Ok((Some(msgs::ClosingSigned {
+ channel_id: self.context.channel_id,
+ fee_satoshis: total_fee_satoshis,
+ signature: sig,
+ fee_range: Some(msgs::ClosingSignedFeeRange {
+ min_fee_satoshis: our_min_fee,
+ max_fee_satoshis: our_max_fee,
+ }),
+ }), None))
+ }
+ }
}
// Marks a channel as waiting for a response from the counterparty. If it's not received
*ticks_elapsed >= DISCONNECT_PEER_AWAITING_RESPONSE_TICKS
}
- pub fn shutdown<SP: Deref>(
+ pub fn shutdown(
&mut self, signer_provider: &SP, their_features: &InitFeatures, msg: &msgs::Shutdown
) -> Result<(Option<msgs::Shutdown>, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), ChannelError>
- where SP::Target: SignerProvider
{
if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
return Err(ChannelError::Close("Peer sent shutdown when we needed a channel_reestablish".to_owned()));
self.build_closing_transaction($new_fee, false)
};
- let sig = self.context.holder_signer
- .sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
-
- let signed_tx = if $new_fee == msg.fee_satoshis {
- self.context.channel_state = ChannelState::ShutdownComplete as u32;
- self.context.update_time_counter += 1;
- let tx = self.build_signed_closing_transaction(&closing_tx, &msg.signature, &sig);
- Some(tx)
- } else { None };
+ return match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let sig = ecdsa
+ .sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
- self.context.last_sent_closing_fee = Some((used_fee, sig.clone()));
- return Ok((Some(msgs::ClosingSigned {
- channel_id: self.context.channel_id,
- fee_satoshis: used_fee,
- signature: sig,
- fee_range: Some(msgs::ClosingSignedFeeRange {
- min_fee_satoshis: our_min_fee,
- max_fee_satoshis: our_max_fee,
- }),
- }), signed_tx))
+ let signed_tx = if $new_fee == msg.fee_satoshis {
+ self.context.channel_state = ChannelState::ShutdownComplete as u32;
+ self.context.update_time_counter += 1;
+ let tx = self.build_signed_closing_transaction(&closing_tx, &msg.signature, &sig);
+ Some(tx)
+ } else { None };
+
+ self.context.last_sent_closing_fee = Some((used_fee, sig.clone()));
+ Ok((Some(msgs::ClosingSigned {
+ channel_id: self.context.channel_id,
+ fee_satoshis: used_fee,
+ signature: sig,
+ fee_range: Some(msgs::ClosingSignedFeeRange {
+ min_fee_satoshis: our_min_fee,
+ max_fee_satoshis: our_max_fee,
+ }),
+ }), signed_tx))
+ }
+ }
}
}
}
#[cfg(test)]
- pub fn get_signer(&self) -> &Signer {
+ pub fn get_signer(&self) -> &ChannelSignerType<<SP::Target as SignerProvider>::Signer> {
&self.context.holder_signer
}
if self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32) == 0 {
if self.context.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
let next_per_commitment_point =
- self.context.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &self.context.secp_ctx);
+ self.context.holder_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &self.context.secp_ctx);
return Some(msgs::ChannelReady {
channel_id: self.context.channel_id,
next_per_commitment_point,
},
Ok(v) => v
};
- let our_bitcoin_sig = match self.context.holder_signer.sign_channel_announcement_with_funding_key(&announcement, &self.context.secp_ctx) {
- Err(_) => {
- log_error!(logger, "Signer rejected channel_announcement signing. Channel will not be announced!");
- return None;
- },
- Ok(v) => v
- };
- let short_channel_id = match self.context.get_short_channel_id() {
- Some(scid) => scid,
- None => return None,
- };
+ match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let our_bitcoin_sig = match ecdsa.sign_channel_announcement_with_funding_key(&announcement, &self.context.secp_ctx) {
+ Err(_) => {
+ log_error!(logger, "Signer rejected channel_announcement signing. Channel will not be announced!");
+ return None;
+ },
+ Ok(v) => v
+ };
+ let short_channel_id = match self.context.get_short_channel_id() {
+ Some(scid) => scid,
+ None => return None,
+ };
- self.context.announcement_sigs_state = AnnouncementSigsState::MessageSent;
+ self.context.announcement_sigs_state = AnnouncementSigsState::MessageSent;
- Some(msgs::AnnouncementSignatures {
- channel_id: self.context.channel_id(),
- short_channel_id,
- node_signature: our_node_sig,
- bitcoin_signature: our_bitcoin_sig,
- })
+ Some(msgs::AnnouncementSignatures {
+ channel_id: self.context.channel_id(),
+ short_channel_id,
+ node_signature: our_node_sig,
+ bitcoin_signature: our_bitcoin_sig,
+ })
+ }
+ }
}
/// Signs the given channel announcement, returning a ChannelError::Ignore if no keys are
let our_node_sig = node_signer.sign_gossip_message(msgs::UnsignedGossipMessage::ChannelAnnouncement(&announcement))
.map_err(|_| ChannelError::Ignore("Failed to generate node signature for channel_announcement".to_owned()))?;
- let our_bitcoin_sig = self.context.holder_signer.sign_channel_announcement_with_funding_key(&announcement, &self.context.secp_ctx)
- .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?;
- Ok(msgs::ChannelAnnouncement {
- node_signature_1: if were_node_one { our_node_sig } else { their_node_sig },
- node_signature_2: if were_node_one { their_node_sig } else { our_node_sig },
- bitcoin_signature_1: if were_node_one { our_bitcoin_sig } else { their_bitcoin_sig },
- bitcoin_signature_2: if were_node_one { their_bitcoin_sig } else { our_bitcoin_sig },
- contents: announcement,
- })
+ match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let our_bitcoin_sig = ecdsa.sign_channel_announcement_with_funding_key(&announcement, &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?;
+ Ok(msgs::ChannelAnnouncement {
+ node_signature_1: if were_node_one { our_node_sig } else { their_node_sig },
+ node_signature_2: if were_node_one { their_node_sig } else { our_node_sig },
+ bitcoin_signature_1: if were_node_one { our_bitcoin_sig } else { their_bitcoin_sig },
+ bitcoin_signature_2: if were_node_one { their_bitcoin_sig } else { our_bitcoin_sig },
+ contents: announcement,
+ })
+ }
+ }
} else {
Err(ChannelError::Ignore("Attempted to sign channel announcement before we'd received announcement_signatures".to_string()))
}
Some(InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info.clone()))
} else { None };
if let Some(state) = new_state {
- log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce {} to AwaitingAnnouncedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce {} to AwaitingAnnouncedRemoteRevoke", &htlc.payment_hash);
htlc.state = state;
}
}
for htlc in self.context.pending_outbound_htlcs.iter_mut() {
if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
- log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
+ log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", &htlc.payment_hash);
// Grab the preimage, if it exists, instead of cloning
let mut reason = OutboundHTLCOutcome::Success(None);
mem::swap(outcome, &mut reason);
let counterparty_keys = self.context.build_remote_transaction_keys();
let commitment_stats = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger);
let counterparty_commitment_txid = commitment_stats.tx.trust().txid();
- let (signature, htlc_signatures);
- {
- let mut htlcs = Vec::with_capacity(commitment_stats.htlcs_included.len());
- for &(ref htlc, _) in commitment_stats.htlcs_included.iter() {
- htlcs.push(htlc);
- }
+ match &self.context.holder_signer {
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let (signature, htlc_signatures);
- let res = self.context.holder_signer.sign_counterparty_commitment(&commitment_stats.tx, commitment_stats.preimages, &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?;
- signature = res.0;
- htlc_signatures = res.1;
+ {
+ let mut htlcs = Vec::with_capacity(commitment_stats.htlcs_included.len());
+ for &(ref htlc, _) in commitment_stats.htlcs_included.iter() {
+ htlcs.push(htlc);
+ }
- log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {} in channel {}",
- encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction),
- &counterparty_commitment_txid, encode::serialize_hex(&self.context.get_funding_redeemscript()),
- log_bytes!(signature.serialize_compact()[..]), log_bytes!(self.context.channel_id()));
+ let res = ecdsa.sign_counterparty_commitment(&commitment_stats.tx, commitment_stats.preimages, &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?;
+ signature = res.0;
+ htlc_signatures = res.1;
+
+ log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {} in channel {}",
+ encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction),
+ &counterparty_commitment_txid, encode::serialize_hex(&self.context.get_funding_redeemscript()),
+ log_bytes!(signature.serialize_compact()[..]), log_bytes!(self.context.channel_id()));
+
+ for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) {
+ log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}",
+ encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.context.get_holder_selected_contest_delay(), htlc, &self.context.channel_type, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)),
+ encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)),
+ log_bytes!(counterparty_keys.broadcaster_htlc_key.serialize()),
+ log_bytes!(htlc_sig.serialize_compact()[..]), log_bytes!(self.context.channel_id()));
+ }
+ }
- for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) {
- log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}",
- encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.context.get_holder_selected_contest_delay(), htlc, &self.context.channel_type, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)),
- encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)),
- log_bytes!(counterparty_keys.broadcaster_htlc_key.serialize()),
- log_bytes!(htlc_sig.serialize_compact()[..]), log_bytes!(self.context.channel_id()));
+ Ok((msgs::CommitmentSigned {
+ channel_id: self.context.channel_id,
+ signature,
+ htlc_signatures,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ }, (counterparty_commitment_txid, commitment_stats.htlcs_included)))
}
}
-
- Ok((msgs::CommitmentSigned {
- channel_id: self.context.channel_id,
- signature,
- htlc_signatures,
- #[cfg(taproot)]
- partial_signature_with_nonce: None,
- }, (counterparty_commitment_txid, commitment_stats.htlcs_included)))
}
/// Adds a pending outbound HTLC to this channel, and builds a new remote commitment
///
/// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
/// [`ChannelMonitorUpdate`] will be returned).
- pub fn get_shutdown<SP: Deref>(&mut self, signer_provider: &SP, their_features: &InitFeatures,
+ pub fn get_shutdown(&mut self, signer_provider: &SP, their_features: &InitFeatures,
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
-> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
- where SP::Target: SignerProvider {
+ {
for htlc in self.context.pending_outbound_htlcs.iter() {
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first".to_owned()});
}
/// A not-yet-funded outbound (from holder) channel using V1 channel establishment.
-pub(super) struct OutboundV1Channel<Signer: ChannelSigner> {
- pub context: ChannelContext<Signer>,
+pub(super) struct OutboundV1Channel<SP: Deref> where SP::Target: SignerProvider {
+ pub context: ChannelContext<SP>,
pub unfunded_context: UnfundedChannelContext,
}
-impl<Signer: WriteableEcdsaChannelSigner> OutboundV1Channel<Signer> {
- pub fn new<ES: Deref, SP: Deref, F: Deref>(
+impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
+ pub fn new<ES: Deref, F: Deref>(
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP, counterparty_node_id: PublicKey, their_features: &InitFeatures,
channel_value_satoshis: u64, push_msat: u64, user_id: u128, config: &UserConfig, current_chain_height: u32,
outbound_scid_alias: u64
- ) -> Result<OutboundV1Channel<Signer>, APIError>
+ ) -> Result<OutboundV1Channel<SP>, APIError>
where ES::Target: EntropySource,
- SP::Target: SignerProvider<Signer = Signer>,
- F::Target: FeeEstimator,
+ F::Target: FeeEstimator
{
let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
latest_monitor_update_id: 0,
- holder_signer,
+ holder_signer: ChannelSignerType::Ecdsa(holder_signer),
shutdown_scriptpubkey,
destination_script,
fn get_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> where L::Target: Logger {
let counterparty_keys = self.context.build_remote_transaction_keys();
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
- Ok(self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
+ match &self.context.holder_signer {
+ // TODO (taproot|arik): move match into calling method for Taproot
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ Ok(ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
+ }
+ }
}
/// Updates channel state with knowledge of the funding transaction's txid/index, and generates
/// Do NOT broadcast the funding transaction until after a successful funding_signed call!
/// If an Err is returned, it is a ChannelError::Close.
pub fn get_funding_created<L: Deref>(mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L)
- -> Result<(Channel<Signer>, msgs::FundingCreated), (Self, ChannelError)> where L::Target: Logger {
+ -> Result<(Channel<SP>, msgs::FundingCreated), (Self, ChannelError)> where L::Target: Logger {
if !self.context.is_outbound() {
panic!("Tried to create outbound funding_created message on an inbound channel!");
}
}
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
- self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
+ self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
let signature = match self.get_funding_created_signature(logger) {
Ok(res) => res,
panic!("Tried to send an open_channel for a channel that has already advanced");
}
- let first_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let first_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
let keys = self.context.get_holder_pubkeys();
msgs::OpenChannel {
}
/// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment.
-pub(super) struct InboundV1Channel<Signer: ChannelSigner> {
- pub context: ChannelContext<Signer>,
+pub(super) struct InboundV1Channel<SP: Deref> where SP::Target: SignerProvider {
+ pub context: ChannelContext<SP>,
pub unfunded_context: UnfundedChannelContext,
}
-impl<Signer: WriteableEcdsaChannelSigner> InboundV1Channel<Signer> {
+impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
/// Creates a new channel from a remote sides' request for one.
/// Assumes chain_hash has already been checked and corresponds with what we expect!
- pub fn new<ES: Deref, SP: Deref, F: Deref, L: Deref>(
+ pub fn new<ES: Deref, F: Deref, L: Deref>(
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
their_features: &InitFeatures, msg: &msgs::OpenChannel, user_id: u128, config: &UserConfig,
current_chain_height: u32, logger: &L, is_0conf: bool,
- ) -> Result<InboundV1Channel<Signer>, ChannelError>
+ ) -> Result<InboundV1Channel<SP>, ChannelError>
where ES::Target: EntropySource,
- SP::Target: SignerProvider<Signer = Signer>,
F::Target: FeeEstimator,
L::Target: Logger,
{
if msg.htlc_minimum_msat >= full_channel_value_msat {
return Err(ChannelError::Close(format!("Minimum htlc value ({}) was larger than full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
}
- Channel::<Signer>::check_remote_fee(&channel_type, fee_estimator, msg.feerate_per_kw, None, logger)?;
+ Channel::<SP>::check_remote_fee(&channel_type, fee_estimator, msg.feerate_per_kw, None, logger)?;
let max_counterparty_selected_contest_delay = u16::min(config.channel_handshake_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT);
if msg.to_self_delay > max_counterparty_selected_contest_delay {
latest_monitor_update_id: 0,
- holder_signer,
+ holder_signer: ChannelSignerType::Ecdsa(holder_signer),
shutdown_scriptpubkey,
destination_script,
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
fn generate_accept_channel_message(&self) -> msgs::AcceptChannel {
- let first_per_commitment_point = self.context.holder_signer.get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
+ let first_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
let keys = self.context.get_holder_pubkeys();
msgs::AcceptChannel {
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
log_bytes!(self.context.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
- let counterparty_signature = self.context.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
- .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
+ match &self.context.holder_signer {
+ // TODO (arik): move match into calling method for Taproot
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ let counterparty_signature = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
+ .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
- // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
- Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
+ // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
+ Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
+ }
+ }
}
- pub fn funding_created<SP: Deref, L: Deref>(
+ pub fn funding_created<L: Deref>(
mut self, msg: &msgs::FundingCreated, best_block: BestBlock, signer_provider: &SP, logger: &L
- ) -> Result<(Channel<Signer>, msgs::FundingSigned, ChannelMonitor<Signer>), (Self, ChannelError)>
+ ) -> Result<(Channel<SP>, msgs::FundingSigned, ChannelMonitor<<SP::Target as SignerProvider>::Signer>), (Self, ChannelError)>
where
- SP::Target: SignerProvider<Signer = Signer>,
L::Target: Logger
{
if self.context.is_outbound() {
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
// This is an externally observable change before we finish all our checks. In particular
// funding_created_signature may fail.
- self.context.holder_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
+ self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Ok(res) => res,
self.context.counterparty_funding_pubkey()
);
- if let Err(_) = self.context.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new()) {
+ if let Err(_) = self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new()) {
return Err((self, ChannelError::Close("Failed to validate our commitment".to_owned())));
}
}
}
-impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
+impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
// Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been
// called.
self.context.latest_monitor_update_id.write(writer)?;
let mut key_data = VecWriter(Vec::new());
- self.context.holder_signer.write(&mut key_data)?;
+ // TODO (taproot|arik): Introduce serialization distinction for non-ECDSA signers.
+ self.context.holder_signer.as_ecdsa().expect("Only ECDSA signers may be serialized").write(&mut key_data)?;
assert!(key_data.0.len() < core::usize::MAX);
assert!(key_data.0.len() < core::u32::MAX as usize);
(key_data.0.len() as u32).write(writer)?;
}
const MAX_ALLOC_SIZE: usize = 64*1024;
-impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c ChannelTypeFeatures)> for Channel<<SP::Target as SignerProvider>::Signer>
+impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c ChannelTypeFeatures)> for Channel<SP>
where
ES::Target: EntropySource,
SP::Target: SignerProvider
latest_monitor_update_id,
- holder_signer,
+ holder_signer: ChannelSignerType::Ecdsa(holder_signer),
shutdown_scriptpubkey,
destination_script,
use crate::chain::transaction::OutPoint;
use crate::routing::router::Path;
use crate::util::config::UserConfig;
- use crate::util::enforcing_trait_impls::EnforcingSigner;
use crate::util::errors::APIError;
use crate::util::test_utils;
- use crate::util::test_utils::OnGetShutdownScriptpubkey;
+ use crate::util::test_utils::{OnGetShutdownScriptpubkey, TestKeysInterface};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::ffi::Signature as FFISignature;
use bitcoin::secp256k1::{SecretKey,PublicKey};
// arithmetic, causing a panic with debug assertions enabled.
let fee_est = TestFeeEstimator { fee_est: 42 };
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&fee_est);
- assert!(Channel::<InMemorySigner>::check_remote_fee(
+ assert!(Channel::<&TestKeysInterface>::check_remote_fee(
&ChannelTypeFeatures::only_static_remote_key(), &bounded_fee_estimator,
u32::max_value(), None, &&test_utils::TestLogger::new()).is_err());
}
let secp_ctx = Secp256k1::new();
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- match OutboundV1Channel::<EnforcingSigner>::new(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
+ match OutboundV1Channel::<&TestKeysInterface>::new(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
Err(APIError::IncompatibleShutdownScript { script }) => {
assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner());
},
let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&bounded_fee_estimator, &&keys_provider, &&keys_provider, node_a_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Now change the fee so we can check that the fee in the open_channel message is the
// same as the old fee.
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let mut node_b_chan = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
// Node B --> Node A: accept channel, explicitly setting B's dust limit.
let mut accept_channel_msg = node_b_chan.accept_inbound_channel();
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut chan = OutboundV1Channel::<EnforcingSigner>::new(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut chan = OutboundV1Channel::<&TestKeysInterface>::new(&fee_est, &&keys_provider, &&keys_provider, node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
let commitment_tx_fee_0_htlcs = commit_tx_fee_msat(chan.context.feerate_per_kw, 0, chan.context.get_channel_type());
let commitment_tx_fee_1_htlc = commit_tx_fee_msat(chan.context.feerate_per_kw, 1, chan.context.get_channel_type());
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
let open_channel_msg = node_a_chan.get_open_channel(chain_hash);
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let mut node_b_chan = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
// Node B --> Node A: accept channel
let accept_channel_msg = node_b_chan.accept_inbound_channel();
// Test that `OutboundV1Channel::new` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
// which is set to the lower bound + 1 (2%) of the `channel_value`.
- let chan_1 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
+ let chan_1 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_2_percent), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
let chan_1_value_msat = chan_1.context.channel_value_satoshis * 1000;
assert_eq!(chan_1.context.holder_max_htlc_value_in_flight_msat, (chan_1_value_msat as f64 * 0.02) as u64);
// Test with the upper bound - 1 of valid values (99%).
- let chan_2 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
+ let chan_2 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_99_percent), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
let chan_2_value_msat = chan_2.context.channel_value_satoshis * 1000;
assert_eq!(chan_2.context.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
// Test that `InboundV1Channel::new` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
// which is set to the lower bound - 1 (2%) of the `channel_value`.
- let chan_3 = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_2_percent), &channelmanager::provided_init_features(&config_2_percent), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let chan_3 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_2_percent), &channelmanager::provided_init_features(&config_2_percent), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
let chan_3_value_msat = chan_3.context.channel_value_satoshis * 1000;
assert_eq!(chan_3.context.holder_max_htlc_value_in_flight_msat, (chan_3_value_msat as f64 * 0.02) as u64);
// Test with the upper bound - 1 of valid values (99%).
- let chan_4 = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_99_percent), &channelmanager::provided_init_features(&config_99_percent), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let chan_4 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_99_percent), &channelmanager::provided_init_features(&config_99_percent), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
let chan_4_value_msat = chan_4.context.channel_value_satoshis * 1000;
assert_eq!(chan_4.context.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64);
// Test that `OutboundV1Channel::new` uses the lower bound of the configurable percentage values (1%)
// if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
- let chan_5 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
+ let chan_5 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_0_percent), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
let chan_5_value_msat = chan_5.context.channel_value_satoshis * 1000;
assert_eq!(chan_5.context.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64);
// Test that `OutboundV1Channel::new` uses the upper bound of the configurable percentage values
// (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
// than 100.
- let chan_6 = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
+ let chan_6 = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&config_101_percent), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
let chan_6_value_msat = chan_6.context.channel_value_satoshis * 1000;
assert_eq!(chan_6.context.holder_max_htlc_value_in_flight_msat, chan_6_value_msat);
// Test that `InboundV1Channel::new` uses the lower bound of the configurable percentage values (1%)
// if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
- let chan_7 = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_0_percent), &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let chan_7 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_0_percent), &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
let chan_7_value_msat = chan_7.context.channel_value_satoshis * 1000;
assert_eq!(chan_7.context.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64);
// Test that `InboundV1Channel::new` uses the upper bound of the configurable percentage values
// (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
// than 100.
- let chan_8 = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_101_percent), &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let chan_8 = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_101_percent), &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, /*is_0conf=*/false).unwrap();
let chan_8_value_msat = chan_8.context.channel_value_satoshis * 1000;
assert_eq!(chan_8.context.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
}
let mut outbound_node_config = UserConfig::default();
outbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (outbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
- let chan = OutboundV1Channel::<EnforcingSigner>::new(&&fee_est, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
+ let chan = OutboundV1Channel::<&TestKeysInterface>::new(&&fee_est, &&keys_provider, &&keys_provider, outbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.context.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64);
assert_eq!(chan.context.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve);
inbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (inbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
if outbound_selected_channel_reserve_perc + inbound_selected_channel_reserve_perc < 1.0 {
- let chan_inbound_node = InboundV1Channel::<EnforcingSigner>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let chan_inbound_node = InboundV1Channel::<&TestKeysInterface>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false).unwrap();
let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.context.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64);
assert_eq!(chan_inbound_node.context.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve);
} else {
// Channel Negotiations failed
- let result = InboundV1Channel::<EnforcingSigner>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false);
+ let result = InboundV1Channel::<&TestKeysInterface>::new(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, /*is_0conf=*/false);
assert!(result.is_err());
}
}
// Create Node A's channel pointing to Node B's pubkey
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
+ let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let mut node_b_chan = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
+ let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
// Node B --> Node A: accept channel, explicitly setting B's dust limit.
let mut accept_channel_msg = node_b_chan.accept_inbound_channel();
let counterparty_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let mut config = UserConfig::default();
config.channel_handshake_config.announced_channel = false;
- let mut chan = OutboundV1Channel::<InMemorySigner>::new(&LowerBoundedFeeEstimator::new(&feeest), &&keys_provider, &&keys_provider, counterparty_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 0, 42, &config, 0, 42).unwrap(); // Nothing uses their network key in this test
+ let mut chan = OutboundV1Channel::<&Keys>::new(&LowerBoundedFeeEstimator::new(&feeest), &&keys_provider, &&keys_provider, counterparty_node_id, &channelmanager::provided_init_features(&config), 10_000_000, 0, 42, &config, 0, 42).unwrap(); // Nothing uses their network key in this test
chan.context.holder_dust_limit_satoshis = 546;
chan.context.counterparty_selected_channel_reserve_satoshis = Some(0); // Filled in in accept_channel
// We can't just use build_holder_transaction_keys here as the per_commitment_secret is not
// derived from a commitment_seed, so instead we copy it here and call
// build_commitment_transaction.
- let delayed_payment_base = &chan.context.holder_signer.pubkeys().delayed_payment_basepoint;
+ let delayed_payment_base = &chan.context.holder_signer.as_ref().pubkeys().delayed_payment_basepoint;
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
- let htlc_basepoint = &chan.context.holder_signer.pubkeys().htlc_basepoint;
+ let htlc_basepoint = &chan.context.holder_signer.as_ref().pubkeys().htlc_basepoint;
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
macro_rules! test_commitment {
commitment_tx.clone(),
counterparty_signature,
counterparty_htlc_sigs,
- &chan.context.holder_signer.pubkeys().funding_pubkey,
+ &chan.context.holder_signer.as_ref().pubkeys().funding_pubkey,
chan.context.counterparty_funding_pubkey()
);
let (holder_sig, htlc_sigs) = signer.sign_holder_commitment_and_htlcs(&holder_commitment_tx, &secp_ctx).unwrap();
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let node_a_chan = OutboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider,
+ let node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider,
node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42).unwrap();
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
let mut open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
open_channel_msg.channel_type = Some(channel_type_features);
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let res = InboundV1Channel::<EnforcingSigner>::new(&feeest, &&keys_provider, &&keys_provider,
+ let res = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider,
node_b_node_id, &channelmanager::provided_channel_type_features(&config),
&channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false);
assert!(res.is_ok());
// It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both
// need to signal it.
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
+ let channel_a = OutboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42,
&config, 0, 42
expected_channel_type.set_static_remote_key_required();
expected_channel_type.set_anchors_zero_fee_htlc_tx_required();
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
+ let channel_a = OutboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
- let channel_b = InboundV1Channel::<EnforcingSigner>::new(
+ let channel_b = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
&channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config),
&open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false
let raw_init_features = static_remote_key_required | simple_anchors_required;
let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec());
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
+ let channel_a = OutboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
// Since A supports both `static_remote_key` and `option_anchors`, but B only accepts
// `static_remote_key`, it will fail the channel.
- let channel_b = InboundV1Channel::<EnforcingSigner>::new(
+ let channel_b = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
&channelmanager::provided_channel_type_features(&config), &init_features_with_simple_anchors,
&open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false
// First, we'll try to open a channel between A and B where A requests a channel type for
// the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by
// B as it's not supported by LDK.
- let channel_a = OutboundV1Channel::<EnforcingSigner>::new(
+ let channel_a = OutboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
let mut open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
open_channel_msg.channel_type = Some(simple_anchors_channel_type.clone());
- let res = InboundV1Channel::<EnforcingSigner>::new(
+ let res = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
&channelmanager::provided_channel_type_features(&config), &simple_anchors_init,
&open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false
// `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the
// original `option_anchors` feature, which should be rejected by A as it's not supported by
// LDK.
- let mut channel_a = OutboundV1Channel::<EnforcingSigner>::new(
+ let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init,
10000000, 100000, 42, &config, 0, 42
).unwrap();
let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
- let channel_b = InboundV1Channel::<EnforcingSigner>::new(
+ let channel_b = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
&channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config),
&open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false
use crate::ln::outbound_payment;
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
use crate::ln::wire::Encode;
-use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
}
}
+impl core::fmt::Display for PaymentId {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ crate::util::logger::DebugBytes(&self.0).fmt(f)
+ }
+}
+
/// An identifier used to uniquely identify an intercepted HTLC to LDK.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
/// State we hold per-peer.
-pub(super) struct PeerState<Signer: ChannelSigner> {
+pub(super) struct PeerState<SP: Deref> where SP::Target: SignerProvider {
/// `channel_id` -> `Channel`.
///
/// Holds all funded channels where the peer is the counterparty.
- pub(super) channel_by_id: HashMap<[u8; 32], Channel<Signer>>,
+ pub(super) channel_by_id: HashMap<[u8; 32], Channel<SP>>,
/// `temporary_channel_id` -> `OutboundV1Channel`.
///
/// Holds all outbound V1 channels where the peer is the counterparty. Once an outbound channel has
/// been assigned a `channel_id`, the entry in this map is removed and one is created in
/// `channel_by_id`.
- pub(super) outbound_v1_channel_by_id: HashMap<[u8; 32], OutboundV1Channel<Signer>>,
+ pub(super) outbound_v1_channel_by_id: HashMap<[u8; 32], OutboundV1Channel<SP>>,
/// `temporary_channel_id` -> `InboundV1Channel`.
///
/// Holds all inbound V1 channels where the peer is the counterparty. Once an inbound channel has
/// been assigned a `channel_id`, the entry in this map is removed and one is created in
/// `channel_by_id`.
- pub(super) inbound_v1_channel_by_id: HashMap<[u8; 32], InboundV1Channel<Signer>>,
+ pub(super) inbound_v1_channel_by_id: HashMap<[u8; 32], InboundV1Channel<SP>>,
/// `temporary_channel_id` -> `InboundChannelRequest`.
///
/// When manual channel acceptance is enabled, this holds all unaccepted inbound channels where
is_connected: bool,
}
-impl <Signer: ChannelSigner> PeerState<Signer> {
+impl <SP: Deref> PeerState<SP> where SP::Target: SignerProvider {
/// Indicates that a peer meets the criteria where we're ok to remove it from our storage.
/// If true is passed for `require_disconnected`, the function will return false if we haven't
/// disconnected from the node already, ie. `PeerState::is_connected` is set to `true`.
///
/// See `ChannelManager` struct-level documentation for lock order requirements.
#[cfg(not(any(test, feature = "_test_utils")))]
- per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<<SP::Target as SignerProvider>::Signer>>>>,
+ per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<SP>>>>,
#[cfg(any(test, feature = "_test_utils"))]
- pub(super) per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<<SP::Target as SignerProvider>::Signer>>>>,
+ pub(super) per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<SP>>>>,
/// The set of events which we need to give to the user to handle. In some cases an event may
/// require some further action after the user handles it (currently only blocking a monitor
self.short_channel_id.or(self.outbound_scid_alias)
}
- fn from_channel_context<Signer: WriteableEcdsaChannelSigner, F: Deref>(
- context: &ChannelContext<Signer>, best_block_height: u32, latest_features: InitFeatures,
+ fn from_channel_context<SP: Deref, F: Deref>(
+ context: &ChannelContext<SP>, best_block_height: u32, latest_features: InitFeatures,
fee_estimator: &LowerBoundedFeeEstimator<F>
) -> Self
- where F::Target: FeeEstimator
+ where
+ SP::Target: SignerProvider,
+ F::Target: FeeEstimator
{
let balance = context.get_available_balances(fee_estimator);
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
Ok(temporary_channel_id)
}
- fn list_funded_channels_with_filter<Fn: FnMut(&(&[u8; 32], &Channel<<SP::Target as SignerProvider>::Signer>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
+ fn list_funded_channels_with_filter<Fn: FnMut(&(&[u8; 32], &Channel<SP>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
// Allocate our best estimate of the number of channels we have in the `res`
// Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
// a scid or a scid alias, and the `id_to_peer` shouldn't be used outside
}
/// Helper function that issues the channel close events
- fn issue_channel_close_events(&self, context: &ChannelContext<<SP::Target as SignerProvider>::Signer>, closure_reason: ClosureReason) {
+ fn issue_channel_close_events(&self, context: &ChannelContext<SP>, closure_reason: ClosureReason) {
let mut pending_events_lock = self.pending_events.lock().unwrap();
match context.unbroadcasted_funding() {
Some(transaction) => {
///
/// [`channel_update`]: msgs::ChannelUpdate
/// [`internal_closing_signed`]: Self::internal_closing_signed
- fn get_channel_update_for_broadcast(&self, chan: &Channel<<SP::Target as SignerProvider>::Signer>) -> Result<msgs::ChannelUpdate, LightningError> {
+ fn get_channel_update_for_broadcast(&self, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
if !chan.context.should_announce() {
return Err(LightningError {
err: "Cannot broadcast a channel_update for a private channel".to_owned(),
///
/// [`channel_update`]: msgs::ChannelUpdate
/// [`internal_closing_signed`]: Self::internal_closing_signed
- fn get_channel_update_for_unicast(&self, chan: &Channel<<SP::Target as SignerProvider>::Signer>) -> Result<msgs::ChannelUpdate, LightningError> {
+ fn get_channel_update_for_unicast(&self, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
log_trace!(self.logger, "Attempting to generate channel update for channel {}", log_bytes!(chan.context.channel_id()));
let short_channel_id = match chan.context.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) {
None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}),
self.get_channel_update_for_onion(short_channel_id, chan)
}
- fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<<SP::Target as SignerProvider>::Signer>) -> Result<msgs::ChannelUpdate, LightningError> {
+ fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
log_trace!(self.logger, "Generating channel update for channel {}", log_bytes!(chan.context.channel_id()));
let were_node_one = self.our_network_pubkey.serialize()[..] < chan.context.get_counterparty_node_id().serialize()[..];
/// Handles the generation of a funding transaction, optionally (for tests) with a function
/// which checks the correctness of the funding transaction given the associated channel.
- fn funding_transaction_generated_intern<FundingOutput: Fn(&OutboundV1Channel<<SP::Target as SignerProvider>::Signer>, &Transaction) -> Result<OutPoint, APIError>>(
+ fn funding_transaction_generated_intern<FundingOutput: Fn(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, APIError>>(
&self, temporary_channel_id: &[u8; 32], counterparty_node_id: &PublicKey, funding_transaction: Transaction, find_funding_output: FundingOutput
) -> Result<(), APIError> {
let per_peer_state = self.per_peer_state.read().unwrap();
routing: PendingHTLCRouting::Forward { onion_packet, .. }, skimmed_fee_msat, ..
},
}) => {
- log_trace!(self.logger, "Adding HTLC from short id {} with payment_hash {} to channel with short id {} after delay", prev_short_channel_id, log_bytes!(payment_hash.0), short_chan_id);
+ log_trace!(self.logger, "Adding HTLC from short id {} with payment_hash {} to channel with short id {} after delay", prev_short_channel_id, &payment_hash, short_chan_id);
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
short_channel_id: prev_short_channel_id,
user_channel_id: Some(prev_user_channel_id),
&self.logger)
{
if let ChannelError::Ignore(msg) = e {
- log_trace!(self.logger, "Failed to forward HTLC with payment_hash {}: {}", log_bytes!(payment_hash.0), msg);
+ log_trace!(self.logger, "Failed to forward HTLC with payment_hash {}: {}", &payment_hash, msg);
} else {
panic!("Stated return value requirements in send_htlc() were not met");
}
});
if $purpose != claimable_payment.purpose {
let log_keysend = |keysend| if keysend { "keysend" } else { "non-keysend" };
- log_trace!(self.logger, "Failing new {} HTLC with payment_hash {} as we already had an existing {} HTLC with the same payment hash", log_keysend(is_keysend), log_bytes!(payment_hash.0), log_keysend(!is_keysend));
+ log_trace!(self.logger, "Failing new {} HTLC with payment_hash {} as we already had an existing {} HTLC with the same payment hash", log_keysend(is_keysend), &payment_hash, log_keysend(!is_keysend));
fail_htlc!(claimable_htlc, payment_hash);
}
if !self.default_configuration.accept_mpp_keysend && is_keysend && !claimable_payment.htlcs.is_empty() {
- log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash and our config states we don't accept MPP keysend", log_bytes!(payment_hash.0));
+ log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash and our config states we don't accept MPP keysend", &payment_hash);
fail_htlc!(claimable_htlc, payment_hash);
}
if let Some(earlier_fields) = &mut claimable_payment.onion_fields {
earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry);
if htlc.total_msat != claimable_htlc.total_msat {
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
- log_bytes!(payment_hash.0), claimable_htlc.total_msat, htlc.total_msat);
+ &payment_hash, claimable_htlc.total_msat, htlc.total_msat);
total_value = msgs::MAX_VALUE_MSAT;
}
if total_value >= msgs::MAX_VALUE_MSAT { break; }
fail_htlc!(claimable_htlc, payment_hash);
} else if total_value - claimable_htlc.sender_intended_value >= claimable_htlc.total_msat {
log_trace!(self.logger, "Failing HTLC with payment_hash {} as payment is already claimable",
- log_bytes!(payment_hash.0));
+ &payment_hash);
fail_htlc!(claimable_htlc, payment_hash);
} else if total_value >= claimable_htlc.total_msat {
#[allow(unused_assignments)] {
let (payment_preimage, min_final_cltv_expiry_delta) = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
Ok(result) => result,
Err(()) => {
- log_trace!(self.logger, "Failing new HTLC with payment_hash {} as payment verification failed", log_bytes!(payment_hash.0));
+ log_trace!(self.logger, "Failing new HTLC with payment_hash {} as payment verification failed", &payment_hash);
fail_htlc!(claimable_htlc, payment_hash);
}
};
let expected_min_expiry_height = (self.current_best_block().height() + min_final_cltv_expiry_delta as u32) as u64;
if (cltv_expiry as u64) < expected_min_expiry_height {
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})",
- log_bytes!(payment_hash.0), cltv_expiry, expected_min_expiry_height);
+ &payment_hash, cltv_expiry, expected_min_expiry_height);
fail_htlc!(claimable_htlc, payment_hash);
}
}
},
hash_map::Entry::Occupied(inbound_payment) => {
if let OnionPayload::Spontaneous(_) = claimable_htlc.onion_payload {
- log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
+ log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", &payment_hash);
fail_htlc!(claimable_htlc, payment_hash);
}
let payment_data = payment_data.unwrap();
if inbound_payment.get().payment_secret != payment_data.payment_secret {
- log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
+ log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", &payment_hash);
fail_htlc!(claimable_htlc, payment_hash);
} else if inbound_payment.get().min_value_msat.is_some() && payment_data.total_msat < inbound_payment.get().min_value_msat.unwrap() {
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our minimum value (had {}, needed {}).",
- log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
+ &payment_hash, payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
fail_htlc!(claimable_htlc, payment_hash);
} else {
let purpose = events::PaymentPurpose::InvoicePayment {
let _ = self.process_background_events();
}
- fn update_channel_fee(&self, chan_id: &[u8; 32], chan: &mut Channel<<SP::Target as SignerProvider>::Signer>, new_feerate: u32) -> NotifyOption {
+ fn update_channel_fee(&self, chan_id: &[u8; 32], chan: &mut Channel<SP>, new_feerate: u32) -> NotifyOption {
if !chan.context.is_outbound() { return NotifyOption::SkipPersist; }
// If the feerate has decreased by less than half, don't bother
if new_feerate <= chan.context.get_feerate_sat_per_1000_weight() && new_feerate * 2 > chan.context.get_feerate_sat_per_1000_weight() {
let process_unfunded_channel_tick = |
chan_id: &[u8; 32],
- chan_context: &mut ChannelContext<<SP::Target as SignerProvider>::Signer>,
+ chan_context: &mut ChannelContext<SP>,
unfunded_chan_context: &mut UnfundedChannelContext,
pending_msg_events: &mut Vec<MessageSendEvent>,
| {
///
/// This is for failures on the channel on which the HTLC was *received*, not failures
/// forwarding
- fn get_htlc_inbound_temp_fail_err_and_data(&self, desired_err_code: u16, chan: &Channel<<SP::Target as SignerProvider>::Signer>) -> (u16, Vec<u8>) {
+ fn get_htlc_inbound_temp_fail_err_and_data(&self, desired_err_code: u16, chan: &Channel<SP>) -> (u16, Vec<u8>) {
// We can't be sure what SCID was used when relaying inbound towards us, so we have to
// guess somewhat. If its a public channel, we figure best to just use the real SCID (as
// we're not leaking that we have a channel with the counterparty), otherwise we try to use
/// Gets an HTLC onion failure code and error data for an `UPDATE` error, given the error code
/// that we want to return and a channel.
- fn get_htlc_temp_fail_err_and_data(&self, desired_err_code: u16, scid: u64, chan: &Channel<<SP::Target as SignerProvider>::Signer>) -> (u16, Vec<u8>) {
+ fn get_htlc_temp_fail_err_and_data(&self, desired_err_code: u16, scid: u64, chan: &Channel<SP>) -> (u16, Vec<u8>) {
debug_assert_eq!(desired_err_code & 0x1000, 0x1000);
if let Ok(upd) = self.get_channel_update_for_onion(scid, chan) {
let mut enc = VecWriter(Vec::with_capacity(upd.serialized_length() + 6));
{ self.push_pending_forwards_ev(); }
},
HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint, .. }) => {
- log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with {:?}", log_bytes!(payment_hash.0), onion_error);
+ log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with {:?}", &payment_hash, onion_error);
let err_packet = onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret);
let mut push_forward_ev = false;
if dup_purpose.is_some() {
debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
- log_bytes!(payment_hash.0));
+ &payment_hash);
}
if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = payment.onion_fields {
if !custom_tlvs_known && custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) {
log_info!(self.logger, "Rejecting payment with payment hash {} as we cannot accept payment with unknown even TLVs: {}",
- log_bytes!(payment_hash.0), log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0)));
+ &payment_hash, log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0)));
claimable_payments.pending_claiming_payments.remove(&payment_hash);
mem::drop(claimable_payments);
for htlc in payment.htlcs {
/// Handles a channel reentering a functional state, either due to reconnect or a monitor
/// update completion.
fn handle_channel_resumption(&self, pending_msg_events: &mut Vec<MessageSendEvent>,
- channel: &mut Channel<<SP::Target as SignerProvider>::Signer>, raa: Option<msgs::RevokeAndACK>,
+ channel: &mut Channel<SP>, raa: Option<msgs::RevokeAndACK>,
commitment_update: Option<msgs::CommitmentUpdate>, order: RAACommitmentOrder,
pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option<Transaction>,
channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>)
/// The filter is called for each peer and provided with the number of unfunded, inbound, and
/// non-0-conf channels we have with the peer.
fn peers_without_funded_channels<Filter>(&self, maybe_count_peer: Filter) -> usize
- where Filter: Fn(&PeerState<<SP::Target as SignerProvider>::Signer>) -> bool {
+ where Filter: Fn(&PeerState<SP>) -> bool {
let mut peers_without_funded_channels = 0;
let best_block_height = self.best_block.read().unwrap().height();
{
}
fn unfunded_channel_count(
- peer: &PeerState<<SP::Target as SignerProvider>::Signer>, best_block_height: u32
+ peer: &PeerState<SP>, best_block_height: u32
) -> usize {
let mut num_unfunded_channels = 0;
for (_, chan) in peer.channel_by_id.iter() {
chan.get().context.config().accept_underpaying_htlcs, next_packet_pk_opt),
Err(e) => PendingHTLCStatus::Fail(e)
};
- let create_pending_htlc_status = |chan: &Channel<<SP::Target as SignerProvider>::Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
+ let create_pending_htlc_status = |chan: &Channel<SP>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match monitor_event {
MonitorEvent::HTLCEvent(htlc_update) => {
if let Some(preimage) = htlc_update.payment_preimage {
- log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", log_bytes!(preimage.0));
+ log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", &preimage);
self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, funding_outpoint);
} else {
- log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
+ log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
/// Calls a function which handles an on-chain event (blocks dis/connected, transactions
/// un/confirmed, etc) on each channel, handling any resulting errors or messages generated by
/// the function.
- fn do_chain_event<FN: Fn(&mut Channel<<SP::Target as SignerProvider>::Signer>) -> Result<(Option<msgs::ChannelReady>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason>>
+ fn do_chain_event<FN: Fn(&mut Channel<SP>) -> Result<(Option<msgs::ChannelReady>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason>>
(&self, height_opt: Option<u32>, f: FN) {
// Note that we MUST NOT end up calling methods on self.chain_monitor here - we're called
// during initialization prior to the chain_monitor being fully configured in some cases.
let channel_count: u64 = Readable::read(reader)?;
let mut funding_txo_set = HashSet::with_capacity(cmp::min(channel_count as usize, 128));
- let mut peer_channels: HashMap<PublicKey, HashMap<[u8; 32], Channel<<SP::Target as SignerProvider>::Signer>>> = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
+ let mut peer_channels: HashMap<PublicKey, HashMap<[u8; 32], Channel<SP>>> = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut id_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut channel_closures = VecDeque::new();
let mut close_background_events = Vec::new();
for _ in 0..channel_count {
- let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (
+ let mut channel: Channel<SP> = Channel::read(reader, (
&args.entropy_source, &args.signer_provider, best_block_height, &provided_channel_type_features(&args.default_config)
))?;
let funding_txo = channel.context.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
// backwards leg of the HTLC will simply be rejected.
log_info!(args.logger,
"Failing HTLC with hash {} as it is missing in the ChannelMonitor for channel {} but was present in the (stale) ChannelManager",
- log_bytes!(channel.context.channel_id()), log_bytes!(payment_hash.0));
+ log_bytes!(channel.context.channel_id()), &payment_hash);
failed_htlcs.push((channel_htlc_source.clone(), *payment_hash, channel.context.get_counterparty_node_id(), channel.context.channel_id()));
}
}
};
let peer_count: u64 = Readable::read(reader)?;
- let mut per_peer_state = HashMap::with_capacity(cmp::min(peer_count as usize, MAX_ALLOC_SIZE/mem::size_of::<(PublicKey, Mutex<PeerState<<SP::Target as SignerProvider>::Signer>>)>()));
+ let mut per_peer_state = HashMap::with_capacity(cmp::min(peer_count as usize, MAX_ALLOC_SIZE/mem::size_of::<(PublicKey, Mutex<PeerState<SP>>)>()));
for _ in 0..peer_count {
let peer_pubkey = Readable::read(reader)?;
let peer_chans = peer_channels.remove(&peer_pubkey).unwrap_or(HashMap::new());
hash_map::Entry::Occupied(mut entry) => {
let newly_added = entry.get_mut().insert(session_priv_bytes, &path);
log_info!(args.logger, "{} a pending payment path for {} msat for session priv {} on an existing pending payment with payment hash {}",
- if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
+ if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), &htlc.payment_hash);
},
hash_map::Entry::Vacant(entry) => {
let path_fee = path.fee_msat();
starting_block_height: best_block_height,
});
log_info!(args.logger, "Added a pending payment for {} msat with payment hash {} for path with session priv {}",
- path_amt, log_bytes!(htlc.payment_hash.0), log_bytes!(session_priv_bytes));
+ path_amt, &htlc.payment_hash, log_bytes!(session_priv_bytes));
}
}
}
if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
if pending_forward_matches_htlc(&htlc_info) {
log_info!(args.logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}",
- log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
+ &htlc.payment_hash, log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
false
} else { true }
} else { true }
pending_intercepted_htlcs.as_mut().unwrap().retain(|intercepted_id, htlc_info| {
if pending_forward_matches_htlc(&htlc_info) {
log_info!(args.logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}",
- log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
+ &htlc.payment_hash, log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
pending_events_read.retain(|(event, _)| {
if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event {
intercepted_id != ev_id
None => match inbound_payment::verify(payment_hash, &hop_data, 0, &expanded_inbound_key, &args.logger) {
Ok((payment_preimage, _)) => payment_preimage,
Err(()) => {
- log_error!(args.logger, "Failed to read claimable payment data for HTLC with payment hash {} - was not a pending inbound payment and didn't match our payment key", log_bytes!(payment_hash.0));
+ log_error!(args.logger, "Failed to read claimable payment data for HTLC with payment hash {} - was not a pending inbound payment and didn't match our payment key", &payment_hash);
return Err(DecodeError::InvalidValue);
}
}
for (_, monitor) in args.channel_monitors.iter() {
for (payment_hash, payment_preimage) in monitor.get_stored_preimages() {
if let Some(payment) = claimable_payments.remove(&payment_hash) {
- log_info!(args.logger, "Re-claiming HTLCs with payment hash {} as we've released the preimage to a ChannelMonitor!", log_bytes!(payment_hash.0));
+ log_info!(args.logger, "Re-claiming HTLCs with payment hash {} as we've released the preimage to a ChannelMonitor!", &payment_hash);
let mut claimable_amt_msat = 0;
let mut receiver_node_id = Some(our_network_pubkey);
let phantom_shared_secret = payment.htlcs[0].prev_hop.phantom_shared_secret;
let events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 0);
}
+
+ #[test]
+ fn test_payment_display() {
+ let payment_id = PaymentId([42; 32]);
+ assert_eq!(format!("{}", &payment_id), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ let payment_hash = PaymentHash([42; 32]);
+ assert_eq!(format!("{}", &payment_hash), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ let payment_preimage = PaymentPreimage([42; 32]);
+ assert_eq!(format!("{}", &payment_preimage), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ }
}
#[cfg(ldk_bench)]
&'a test_utils::TestFeeEstimator, &'a test_utils::TestRouter<'a>,
&'a test_utils::TestLogger>;
- struct ANodeHolder<'a, P: Persist<InMemorySigner>> {
- node: &'a Manager<'a, P>,
+ struct ANodeHolder<'node_cfg, 'chan_mon_cfg: 'node_cfg, P: Persist<InMemorySigner>> {
+ node: &'node_cfg Manager<'chan_mon_cfg, P>,
}
- impl<'a, P: Persist<InMemorySigner>> NodeHolder for ANodeHolder<'a, P> {
- type CM = Manager<'a, P>;
+ impl<'node_cfg, 'chan_mon_cfg: 'node_cfg, P: Persist<InMemorySigner>> NodeHolder for ANodeHolder<'node_cfg, 'chan_mon_cfg, P> {
+ type CM = Manager<'chan_mon_cfg, P>;
#[inline]
- fn node(&self) -> &Manager<'a, P> { self.node }
+ fn node(&self) -> &Manager<'chan_mon_cfg, P> { self.node }
#[inline]
fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { None }
}
// Byte 2
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
// Byte 3
- ShutdownAnySegwit,
+ ShutdownAnySegwit | Taproot,
// Byte 4
OnionMessages,
// Byte 5
// Byte 2
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
// Byte 3
- ShutdownAnySegwit,
+ ShutdownAnySegwit | Taproot,
// Byte 4
OnionMessages,
// Byte 5
// Byte 2
AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
// Byte 3
- ,
+ Taproot,
// Byte 4
,
// Byte 5
define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext],
"Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional,
set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit);
+ define_feature!(31, Taproot, [InitContext, NodeContext, ChannelTypeContext],
+ "Feature flags for `option_taproot`.", set_taproot_optional,
+ set_taproot_required, supports_taproot, requires_taproot);
define_feature!(39, OnionMessages, [InitContext, NodeContext],
"Feature flags for `option_onion_messages`.", set_onion_messages_optional,
set_onion_messages_required, supports_onion_messages, requires_onion_messages);
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
-use crate::sign::{ChannelSigner, EcdsaChannelSigner, EntropySource};
+use crate::sign::{EcdsaChannelSigner, EntropySource};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel};
let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap();
let local_chan = chan_lock.channel_by_id.get(&chan.2).unwrap();
let chan_signer = local_chan.get_signer();
- let pubkeys = chan_signer.pubkeys();
+ let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
pubkeys.funding_pubkey)
};
let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
let remote_chan = chan_lock.channel_by_id.get(&chan.2).unwrap();
let chan_signer = remote_chan.get_signer();
- let pubkeys = chan_signer.pubkeys();
+ let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
- chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
+ chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
pubkeys.funding_pubkey)
};
&mut htlcs,
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
);
- local_chan_signer.sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
+ local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
};
let commit_signed_msg = msgs::CommitmentSigned {
let local_chan = chan_lock.channel_by_id.get(&chan.2).unwrap();
let chan_signer = local_chan.get_signer();
// Make the signer believe we validated another commitment, so we can release the secret
- chan_signer.get_enforcement_state().last_holder_commitment -= 1;
+ chan_signer.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
- let pubkeys = chan_signer.pubkeys();
+ let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
- chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
- chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx),
- chan_signer.pubkeys().funding_pubkey)
+ chan_signer.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
+ chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx),
+ chan_signer.as_ref().pubkeys().funding_pubkey)
};
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
let per_peer_state = nodes[1].node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
let remote_chan = chan_lock.channel_by_id.get(&chan.2).unwrap();
let chan_signer = remote_chan.get_signer();
- let pubkeys = chan_signer.pubkeys();
+ let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
- chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
- chan_signer.pubkeys().funding_pubkey)
+ chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
+ chan_signer.as_ref().pubkeys().funding_pubkey)
};
// Assemble the set of keys we can use for signatures for our commitment_signed message.
&mut vec![(accepted_htlc_info, ())],
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
);
- local_chan_signer.sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
+ local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
};
let commit_signed_msg = msgs::CommitmentSigned {
let secp_ctx = Secp256k1::new();
for event in events.drain(..) {
match event {
- Event::SpendableOutputs { mut outputs } => {
+ Event::SpendableOutputs { mut outputs, channel_id: _ } => {
for outp in outputs.drain(..) {
txn.push($keysinterface.backing.spend_spendable_outputs(&[&outp], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &secp_ctx).unwrap());
all_outputs.push(outp);
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Remote side tried to send a 0-msat HTLC".to_string(), 1);
check_closed_broadcast!(nodes[1], true).unwrap();
check_added_monitors!(nodes[1], 1);
- check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Remote side tried to send a 0-msat HTLC".to_string() },
+ check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Remote side tried to send a 0-msat HTLC".to_string() },
[nodes[0].node.get_our_node_id()], 100000);
}
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
// Make signer believe we got a counterparty signature, so that it allows the revocation
- keys.get_enforcement_state().last_holder_commitment -= 1;
- per_commitment_secret = keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
+ keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
+ per_commitment_secret = keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
// Must revoke without gaps
- keys.get_enforcement_state().last_holder_commitment -= 1;
- keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
+ keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
+ keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
- keys.get_enforcement_state().last_holder_commitment -= 1;
+ keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
- &SecretKey::from_slice(&keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
+ &SecretKey::from_slice(&keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
}
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(),
// - one from an RAA and one from an inbound commitment_signed.
let chanmon_cfgs = create_chanmon_cfgs(3);
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let (persister, chain_monitor);
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
- let (persister, chain_monitor, nodes_0_deserialized);
+ let nodes_0_deserialized;
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
hmac.input(&metadata_bytes[..]);
hmac.input(&payment_hash.0);
if !fixed_time_eq(&iv_bytes, &Hmac::from_engine(hmac).into_inner().split_at_mut(IV_LEN).0) {
- log_trace!(logger, "Failing HTLC with user-generated payment_hash {}: unexpected payment_secret", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Failing HTLC with user-generated payment_hash {}: unexpected payment_secret", &payment_hash);
return Err(())
}
},
match derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys) {
Ok(preimage) => payment_preimage = Some(preimage),
Err(bad_preimage_bytes) => {
- log_trace!(logger, "Failing HTLC with payment_hash {} due to mismatching preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes));
+ log_trace!(logger, "Failing HTLC with payment_hash {} due to mismatching preimage {}", &payment_hash, log_bytes!(bad_preimage_bytes));
return Err(())
}
}
},
Err(unknown_bits) => {
- log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", log_bytes!(payment_hash.0), unknown_bits);
+ log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", &payment_hash, unknown_bits);
return Err(());
}
}
let expiry = u64::from_be_bytes(expiry_bytes.try_into().unwrap());
if payment_data.total_msat < min_amt_msat {
- log_trace!(logger, "Failing HTLC with payment_hash {} due to total_msat {} being less than the minimum amount of {} msat", log_bytes!(payment_hash.0), payment_data.total_msat, min_amt_msat);
+ log_trace!(logger, "Failing HTLC with payment_hash {} due to total_msat {} being less than the minimum amount of {} msat", &payment_hash, payment_data.total_msat, min_amt_msat);
return Err(())
}
if expiry < highest_seen_timestamp {
- log_trace!(logger, "Failing HTLC with payment_hash {}: expired payment", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Failing HTLC with payment_hash {}: expired payment", &payment_hash);
return Err(())
}
Ok(Method::LdkPaymentHash) | Ok(Method::LdkPaymentHashCustomFinalCltv) => {
derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys)
.map_err(|bad_preimage_bytes| APIError::APIMisuseError {
- err: format!("Payment hash {} did not match decoded preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes))
+ err: format!("Payment hash {} did not match decoded preimage {}", &payment_hash, log_bytes!(bad_preimage_bytes))
})
},
Ok(Method::UserPaymentHash) | Ok(Method::UserPaymentHashCustomFinalCltv) => Err(APIError::APIMisuseError {
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentHash(pub [u8; 32]);
+
+impl core::fmt::Display for PaymentHash {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ crate::util::logger::DebugBytes(&self.0).fmt(f)
+ }
+}
+
/// payment_preimage type, use to route payment between hop
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentPreimage(pub [u8; 32]);
+
+impl core::fmt::Display for PaymentPreimage {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ crate::util::logger::DebugBytes(&self.0).fmt(f)
+ }
+}
+
/// payment_secret type, use to authenticate sender to the receiver and tie MPP HTLCs together
///
/// This is not exported to bindings users as we just use [u8; 32] directly
use crate::events::bump_transaction::{BumpTransactionEvent, WalletSource};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
use crate::ln::channel;
-use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, PaymentId, RecipientOnionFields};
+use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId, RecipientOnionFields};
use crate::ln::msgs::ChannelMessageHandler;
use crate::util::config::UserConfig;
use crate::util::crypto::sign;
fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_tx: &Transaction) {
let mut spendable = node.chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(spendable.len(), 1);
- if let Event::SpendableOutputs { outputs } = spendable.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, .. } = spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
let spend_tx = node.keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
let spendable_output_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(spendable_output_events.len(), 2);
for (idx, event) in spendable_output_events.iter().enumerate() {
- if let Event::SpendableOutputs { outputs } = event {
+ if let Event::SpendableOutputs { outputs, channel_id } = event {
assert_eq!(outputs.len(), 1);
+ assert!(vec![chan_b.2, chan_a.2].contains(&channel_id.unwrap()));
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(
&[&outputs[0]], Vec::new(), Script::new_op_return(&[]), 253, None, &Secp256k1::new(),
).unwrap();
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
- log_info!(logger, "Result sending payment with id {}: {:?}", log_bytes!(payment_id.0), res);
+ log_info!(logger, "Result sending payment with id {}: {:?}", &payment_id, res);
if let Err(e) = res {
self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, &inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, &send_payment_along_path);
}
{
#[cfg(feature = "std")] {
if has_expired(&route_params) {
- log_error!(logger, "Payment params expired on retry, abandoning payment {}", log_bytes!(payment_id.0));
+ log_error!(logger, "Payment params expired on retry, abandoning payment {}", &payment_id);
self.abandon_payment(payment_id, PaymentFailureReason::PaymentExpired, pending_events);
return
}
) {
Ok(route) => route,
Err(e) => {
- log_error!(logger, "Failed to find a route on retry, abandoning payment {}: {:#?}", log_bytes!(payment_id.0), e);
+ log_error!(logger, "Failed to find a route on retry, abandoning payment {}: {:#?}", &payment_id, e);
self.abandon_payment(payment_id, PaymentFailureReason::RouteNotFound, pending_events);
return
}
},
};
if !payment.get().is_retryable_now() {
- log_error!(logger, "Retries exhausted for payment id {}", log_bytes!(payment_id.0));
+ log_error!(logger, "Retries exhausted for payment id {}", &payment_id);
abandon_with_entry!(payment, PaymentFailureReason::RetriesExhausted);
return
}
res
},
hash_map::Entry::Vacant(_) => {
- log_error!(logger, "Payment with ID {} not found", log_bytes!(payment_id.0));
+ log_error!(logger, "Payment with ID {} not found", &payment_id);
return
}
}
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage,
payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
&send_payment_along_path);
- log_info!(logger, "Result retrying payment id {}: {:?}", log_bytes!(payment_id.0), res);
+ log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path);
}
}
}
} else {
- log_trace!(logger, "Received duplicative fulfill for HTLC with payment_preimage {}", log_bytes!(payment_preimage.0));
+ log_trace!(logger, "Received duplicative fulfill for HTLC with payment_preimage {}", &payment_preimage);
}
}
let mut pending_retry_ev = false;
let attempts_remaining = if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
- log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", &payment_hash);
return false
}
if payment.get().is_fulfilled() {
- log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", &payment_hash);
return false
}
let mut is_retryable_now = payment.get().is_auto_retryable_now();
}
is_retryable_now
} else {
- log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", &payment_hash);
return false
};
core::mem::drop(outbounds);
- log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
+ log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", &payment_hash);
let path_failure = {
if payment_is_probe {
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason, PaymentPurpose};
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
-use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
+use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
use crate::ln::features::Bolt11InvoiceFeatures;
use crate::ln::{msgs, PaymentSecret, PaymentPreimage};
use crate::ln::msgs::ChannelMessageHandler;
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
- check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}",
+ check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}",
&nodes[1].node.get_our_node_id())) }, [nodes[0].node.get_our_node_id()], 100000);
check_added_monitors!(nodes[1], 1);
assert_eq!(nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0).len(), 1);
use crate::chain::ChannelMonitorUpdateStatus;
use crate::sign::NodeSigner;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
-use crate::ln::channelmanager::{ChannelManager, MIN_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields};
+use crate::ln::channelmanager::{MIN_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields};
use crate::routing::gossip::RoutingFees;
use crate::routing::router::{PaymentParameters, RouteHint, RouteHintHop};
use crate::ln::features::ChannelTypeFeatures;
use crate::chain::transaction::OutPoint;
use crate::chain::Confirm;
use crate::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination};
-use crate::ln::channelmanager::ChannelManager;
use crate::ln::msgs::{ChannelMessageHandler, Init};
use crate::util::test_utils;
use crate::util::ser::Writeable;
let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(node_a_spendable.len(), 1);
- if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, channel_id } = node_a_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
+ assert_eq!(channel_id, Some(chan_id));
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, remote_txn_b[0]);
let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(node_b_spendable.len(), 1);
- if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, channel_id } = node_b_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
+ assert_eq!(channel_id, Some(chan_id));
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, remote_txn_a[0]);
//! .allow_mpp()
//! .fallback_v0_p2wpkh(&wpubkey_hash)
//! .build()?
-//! .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+//! .sign::<_, Infallible>(
+//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//! )
//! .expect("failed verifying signature")
//! .write(&mut buffer)
//! .unwrap();
//! .allow_mpp()
//! .fallback_v0_p2wpkh(&wpubkey_hash)
//! .build()?
-//! .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+//! .sign::<_, Infallible>(
+//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//! )
//! .expect("failed verifying signature")
//! .write(&mut buffer)
//! .unwrap();
use bitcoin::hash_types::{WPubkeyHash, WScriptHash};
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
-use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, self};
+use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
use bitcoin::secp256k1::schnorr::Signature;
use bitcoin::util::address::{Address, Payload, WitnessVersion};
use bitcoin::util::schnorr::TweakedPublicKey;
-use core::convert::{Infallible, TryFrom};
+use core::convert::{AsRef, Infallible, TryFrom};
use core::time::Duration;
use crate::io;
use crate::blinded_path::BlindedPath;
use crate::ln::PaymentHash;
-use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
+use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::DecodeError;
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
-use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TlvStream, WithoutSignatures, self};
-use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
+use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents};
const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(7200);
-pub(super) const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice", "signature");
+/// Tag for the hash function used when signing a [`Bolt12Invoice`]'s merkle root.
+pub const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice", "signature");
/// Builds a [`Bolt12Invoice`] from either:
/// - an [`InvoiceRequest`] for the "offer to be paid" flow or
pub struct InvoiceBuilder<'a, S: SigningPubkeyStrategy> {
invreq_bytes: &'a Vec<u8>,
invoice: InvoiceContents,
- keys: Option<KeyPair>,
- signing_pubkey_strategy: core::marker::PhantomData<S>,
+ signing_pubkey_strategy: S,
}
/// Indicates how [`Bolt12Invoice::signing_pubkey`] was set.
/// [`Bolt12Invoice::signing_pubkey`] was derived.
///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-pub struct DerivedSigningPubkey {}
+pub struct DerivedSigningPubkey(KeyPair);
impl SigningPubkeyStrategy for ExplicitSigningPubkey {}
impl SigningPubkeyStrategy for DerivedSigningPubkey {}
),
};
- Self::new(&invoice_request.bytes, contents, None)
+ Self::new(&invoice_request.bytes, contents, ExplicitSigningPubkey {})
}
pub(super) fn for_refund(
),
};
- Self::new(&refund.bytes, contents, None)
+ Self::new(&refund.bytes, contents, ExplicitSigningPubkey {})
}
}
),
};
- Self::new(&invoice_request.bytes, contents, Some(keys))
+ Self::new(&invoice_request.bytes, contents, DerivedSigningPubkey(keys))
}
pub(super) fn for_refund_using_keys(
),
};
- Self::new(&refund.bytes, contents, Some(keys))
+ Self::new(&refund.bytes, contents, DerivedSigningPubkey(keys))
}
}
}
fn new(
- invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, keys: Option<KeyPair>
+ invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: S
) -> Result<Self, Bolt12SemanticError> {
if contents.fields().payment_paths.is_empty() {
return Err(Bolt12SemanticError::MissingPaths);
}
- Ok(Self {
- invreq_bytes,
- invoice: contents,
- keys,
- signing_pubkey_strategy: core::marker::PhantomData,
- })
+ Ok(Self { invreq_bytes, invoice: contents, signing_pubkey_strategy })
}
/// Sets the [`Bolt12Invoice::relative_expiry`] as seconds since [`Bolt12Invoice::created_at`].
self
}
- /// Sets [`Bolt12Invoice::features`] to indicate MPP may be used. Otherwise, MPP is disallowed.
+ /// Sets [`Bolt12Invoice::invoice_features`] to indicate MPP may be used. Otherwise, MPP is
+ /// disallowed.
pub fn allow_mpp(mut self) -> Self {
self.invoice.fields_mut().features.set_basic_mpp_optional();
self
impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
/// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
/// [`UnsignedBolt12Invoice::sign`].
- pub fn build(self) -> Result<UnsignedBolt12Invoice<'a>, Bolt12SemanticError> {
+ pub fn build(self) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
#[cfg(feature = "std")] {
if self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
- Ok(UnsignedBolt12Invoice { invreq_bytes, invoice })
+ Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
}
}
}
- let InvoiceBuilder { invreq_bytes, invoice, keys, .. } = self;
- let unsigned_invoice = UnsignedBolt12Invoice { invreq_bytes, invoice };
+ let InvoiceBuilder {
+ invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
+ } = self;
+ let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
- let keys = keys.unwrap();
let invoice = unsigned_invoice
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+ )
.unwrap();
Ok(invoice)
}
}
/// A semantically valid [`Bolt12Invoice`] that hasn't been signed.
-pub struct UnsignedBolt12Invoice<'a> {
- invreq_bytes: &'a Vec<u8>,
- invoice: InvoiceContents,
+///
+/// # Serialization
+///
+/// This is serialized as a TLV stream, which includes TLV records from the originating message. As
+/// such, it may include unknown, odd TLV records.
+pub struct UnsignedBolt12Invoice {
+ bytes: Vec<u8>,
+ contents: InvoiceContents,
+ tagged_hash: TaggedHash,
}
-impl<'a> UnsignedBolt12Invoice<'a> {
- /// The public key corresponding to the key needed to sign the invoice.
- pub fn signing_pubkey(&self) -> PublicKey {
- self.invoice.fields().signing_pubkey
- }
-
- /// Signs the invoice using the given function.
- ///
- /// This is not exported to bindings users as functions aren't currently mapped.
- pub fn sign<F, E>(self, sign: F) -> Result<Bolt12Invoice, SignError<E>>
- where
- F: FnOnce(&Message) -> Result<Signature, E>
- {
+impl UnsignedBolt12Invoice {
+ fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self {
// Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
// have contained unknown TLV records, which are not stored in `InvoiceRequestContents` or
// `RefundContents`.
- let (_, _, _, invoice_tlv_stream) = self.invoice.as_tlv_stream();
- let invoice_request_bytes = WithoutSignatures(self.invreq_bytes);
+ let (_, _, _, invoice_tlv_stream) = contents.as_tlv_stream();
+ let invoice_request_bytes = WithoutSignatures(invreq_bytes);
let unsigned_tlv_stream = (invoice_request_bytes, invoice_tlv_stream);
let mut bytes = Vec::new();
unsigned_tlv_stream.write(&mut bytes).unwrap();
- let pubkey = self.invoice.fields().signing_pubkey;
- let signature = merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?;
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Self { bytes, contents, tagged_hash }
+ }
+
+ /// Returns the [`TaggedHash`] of the invoice to sign.
+ pub fn tagged_hash(&self) -> &TaggedHash {
+ &self.tagged_hash
+ }
+
+ /// Signs the [`TaggedHash`] of the invoice using the given function.
+ ///
+ /// Note: The hash computation may have included unknown, odd TLV records.
+ ///
+ /// This is not exported to bindings users as functions aren't currently mapped.
+ pub fn sign<F, E>(mut self, sign: F) -> Result<Bolt12Invoice, SignError<E>>
+ where
+ F: FnOnce(&Self) -> Result<Signature, E>
+ {
+ let pubkey = self.contents.fields().signing_pubkey;
+ let signature = merkle::sign_message(sign, &self, pubkey)?;
// Append the signature TLV record to the bytes.
let signature_tlv_stream = SignatureTlvStreamRef {
signature: Some(&signature),
};
- signature_tlv_stream.write(&mut bytes).unwrap();
+ signature_tlv_stream.write(&mut self.bytes).unwrap();
Ok(Bolt12Invoice {
- bytes,
- contents: self.invoice,
+ bytes: self.bytes,
+ contents: self.contents,
signature,
})
}
}
+impl AsRef<TaggedHash> for UnsignedBolt12Invoice {
+ fn as_ref(&self) -> &TaggedHash {
+ &self.tagged_hash
+ }
+}
+
/// A `Bolt12Invoice` is a payment request, typically corresponding to an [`Offer`] or a [`Refund`].
///
/// An invoice may be sent in response to an [`InvoiceRequest`] in the case of an offer or sent
signing_pubkey: PublicKey,
}
-impl Bolt12Invoice {
- /// A complete description of the purpose of the originating offer or refund. Intended to be
- /// displayed to the user but with the caveat that it has not been verified in any way.
- pub fn description(&self) -> PrintableString {
- self.contents.description()
+macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
+ /// The chains that may be used when paying a requested invoice.
+ ///
+ /// From [`Offer::chains`]; `None` if the invoice was created in response to a [`Refund`].
+ ///
+ /// [`Offer::chains`]: crate::offers::offer::Offer::chains
+ pub fn offer_chains(&$self) -> Option<Vec<ChainHash>> {
+ $contents.offer_chains()
+ }
+
+ /// The chain that must be used when paying the invoice; selected from [`offer_chains`] if the
+ /// invoice originated from an offer.
+ ///
+ /// From [`InvoiceRequest::chain`] or [`Refund::chain`].
+ ///
+ /// [`offer_chains`]: Self::offer_chains
+ /// [`InvoiceRequest::chain`]: crate::offers::invoice_request::InvoiceRequest::chain
+ pub fn chain(&$self) -> ChainHash {
+ $contents.chain()
+ }
+
+ /// Opaque bytes set by the originating [`Offer`].
+ ///
+ /// From [`Offer::metadata`]; `None` if the invoice was created in response to a [`Refund`] or
+ /// if the [`Offer`] did not set it.
+ ///
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`Offer::metadata`]: crate::offers::offer::Offer::metadata
+ pub fn metadata(&$self) -> Option<&Vec<u8>> {
+ $contents.metadata()
+ }
+
+ /// The minimum amount required for a successful payment of a single item.
+ ///
+ /// From [`Offer::amount`]; `None` if the invoice was created in response to a [`Refund`] or if
+ /// the [`Offer`] did not set it.
+ ///
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`Offer::amount`]: crate::offers::offer::Offer::amount
+ pub fn amount(&$self) -> Option<&Amount> {
+ $contents.amount()
+ }
+
+ /// Features pertaining to the originating [`Offer`].
+ ///
+ /// From [`Offer::offer_features`]; `None` if the invoice was created in response to a
+ /// [`Refund`].
+ ///
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`Offer::offer_features`]: crate::offers::offer::Offer::offer_features
+ pub fn offer_features(&$self) -> Option<&OfferFeatures> {
+ $contents.offer_features()
+ }
+
+ /// A complete description of the purpose of the originating offer or refund.
+ ///
+ /// From [`Offer::description`] or [`Refund::description`].
+ ///
+ /// [`Offer::description`]: crate::offers::offer::Offer::description
+ pub fn description(&$self) -> PrintableString {
+ $contents.description()
+ }
+
+ /// Duration since the Unix epoch when an invoice should no longer be requested.
+ ///
+ /// From [`Offer::absolute_expiry`] or [`Refund::absolute_expiry`].
+ ///
+ /// [`Offer::absolute_expiry`]: crate::offers::offer::Offer::absolute_expiry
+ pub fn absolute_expiry(&$self) -> Option<Duration> {
+ $contents.absolute_expiry()
+ }
+
+ /// The issuer of the offer or refund.
+ ///
+ /// From [`Offer::issuer`] or [`Refund::issuer`].
+ ///
+ /// [`Offer::issuer`]: crate::offers::offer::Offer::issuer
+ pub fn issuer(&$self) -> Option<PrintableString> {
+ $contents.issuer()
+ }
+
+ /// Paths to the recipient originating from publicly reachable nodes.
+ ///
+ /// From [`Offer::paths`] or [`Refund::paths`].
+ ///
+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
+ pub fn message_paths(&$self) -> &[BlindedPath] {
+ $contents.message_paths()
+ }
+
+ /// The quantity of items supported.
+ ///
+ /// From [`Offer::supported_quantity`]; `None` if the invoice was created in response to a
+ /// [`Refund`].
+ ///
+ /// [`Offer::supported_quantity`]: crate::offers::offer::Offer::supported_quantity
+ pub fn supported_quantity(&$self) -> Option<Quantity> {
+ $contents.supported_quantity()
+ }
+
+ /// An unpredictable series of bytes from the payer.
+ ///
+ /// From [`InvoiceRequest::payer_metadata`] or [`Refund::payer_metadata`].
+ pub fn payer_metadata(&$self) -> &[u8] {
+ $contents.payer_metadata()
+ }
+
+ /// Features pertaining to requesting an invoice.
+ ///
+ /// From [`InvoiceRequest::invoice_request_features`] or [`Refund::features`].
+ pub fn invoice_request_features(&$self) -> &InvoiceRequestFeatures {
+ &$contents.invoice_request_features()
+ }
+
+ /// The quantity of items requested or refunded for.
+ ///
+ /// From [`InvoiceRequest::quantity`] or [`Refund::quantity`].
+ pub fn quantity(&$self) -> Option<u64> {
+ $contents.quantity()
+ }
+
+ /// A possibly transient pubkey used to sign the invoice request or to send an invoice for a
+ /// refund in case there are no [`message_paths`].
+ ///
+ /// [`message_paths`]: Self::message_paths
+ pub fn payer_id(&$self) -> PublicKey {
+ $contents.payer_id()
+ }
+
+ /// A payer-provided note reflected back in the invoice.
+ ///
+ /// From [`InvoiceRequest::payer_note`] or [`Refund::payer_note`].
+ pub fn payer_note(&$self) -> Option<PrintableString> {
+ $contents.payer_note()
}
/// Paths to the recipient originating from publicly reachable nodes, including information
///
/// This is not exported to bindings users as slices with non-reference types cannot be ABI
/// matched in another language.
- pub fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
- &self.contents.fields().payment_paths[..]
+ pub fn payment_paths(&$self) -> &[(BlindedPayInfo, BlindedPath)] {
+ $contents.payment_paths()
}
/// Duration since the Unix epoch when the invoice was created.
- pub fn created_at(&self) -> Duration {
- self.contents.fields().created_at
+ pub fn created_at(&$self) -> Duration {
+ $contents.created_at()
}
/// Duration since [`Bolt12Invoice::created_at`] when the invoice has expired and therefore
/// should no longer be paid.
- pub fn relative_expiry(&self) -> Duration {
- self.contents.fields().relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
+ pub fn relative_expiry(&$self) -> Duration {
+ $contents.relative_expiry()
}
/// Whether the invoice has expired.
#[cfg(feature = "std")]
- pub fn is_expired(&self) -> bool {
- let absolute_expiry = self.created_at().checked_add(self.relative_expiry());
- match absolute_expiry {
- Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
- Ok(elapsed) => elapsed > seconds_from_epoch,
- Err(_) => false,
- },
- None => false,
- }
+ pub fn is_expired(&$self) -> bool {
+ $contents.is_expired()
}
/// SHA256 hash of the payment preimage that will be given in return for paying the invoice.
- pub fn payment_hash(&self) -> PaymentHash {
- self.contents.fields().payment_hash
+ pub fn payment_hash(&$self) -> PaymentHash {
+ $contents.payment_hash()
}
/// The minimum amount required for a successful payment of the invoice.
- pub fn amount_msats(&self) -> u64 {
- self.contents.fields().amount_msats
+ pub fn amount_msats(&$self) -> u64 {
+ $contents.amount_msats()
}
/// Fallback addresses for paying the invoice on-chain, in order of most-preferred to
/// least-preferred.
- pub fn fallbacks(&self) -> Vec<Address> {
- let network = match self.network() {
- None => return Vec::new(),
- Some(network) => network,
- };
-
- let to_valid_address = |address: &FallbackAddress| {
- let version = match WitnessVersion::try_from(address.version) {
- Ok(version) => version,
- Err(_) => return None,
- };
-
- let program = &address.program;
- if program.len() < 2 || program.len() > 40 {
- return None;
- }
-
- let address = Address {
- payload: Payload::WitnessProgram {
- version,
- program: address.program.clone(),
- },
- network,
- };
-
- if !address.is_standard() && version == WitnessVersion::V0 {
- return None;
- }
-
- Some(address)
- };
-
- self.contents.fields().fallbacks
- .as_ref()
- .map(|fallbacks| fallbacks.iter().filter_map(to_valid_address).collect())
- .unwrap_or_else(Vec::new)
- }
-
- fn network(&self) -> Option<Network> {
- let chain = self.contents.chain();
- if chain == ChainHash::using_genesis_block(Network::Bitcoin) {
- Some(Network::Bitcoin)
- } else if chain == ChainHash::using_genesis_block(Network::Testnet) {
- Some(Network::Testnet)
- } else if chain == ChainHash::using_genesis_block(Network::Signet) {
- Some(Network::Signet)
- } else if chain == ChainHash::using_genesis_block(Network::Regtest) {
- Some(Network::Regtest)
- } else {
- None
- }
+ pub fn fallbacks(&$self) -> Vec<Address> {
+ $contents.fallbacks()
}
/// Features pertaining to paying an invoice.
- pub fn features(&self) -> &Bolt12InvoiceFeatures {
- &self.contents.fields().features
+ pub fn invoice_features(&$self) -> &Bolt12InvoiceFeatures {
+ $contents.features()
}
/// The public key corresponding to the key used to sign the invoice.
- pub fn signing_pubkey(&self) -> PublicKey {
- self.contents.fields().signing_pubkey
+ pub fn signing_pubkey(&$self) -> PublicKey {
+ $contents.signing_pubkey()
}
+} }
+
+impl UnsignedBolt12Invoice {
+ invoice_accessors!(self, self.contents);
+}
+
+impl Bolt12Invoice {
+ invoice_accessors!(self, self.contents);
/// Signature of the invoice verified using [`Bolt12Invoice::signing_pubkey`].
pub fn signature(&self) -> Signature {
}
}
+ fn offer_chains(&self) -> Option<Vec<ChainHash>> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } =>
+ Some(invoice_request.inner.offer.chains()),
+ InvoiceContents::ForRefund { .. } => None,
+ }
+ }
+
fn chain(&self) -> ChainHash {
match self {
InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.chain(),
}
}
+ fn metadata(&self) -> Option<&Vec<u8>> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } =>
+ invoice_request.inner.offer.metadata(),
+ InvoiceContents::ForRefund { .. } => None,
+ }
+ }
+
+ fn amount(&self) -> Option<&Amount> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } =>
+ invoice_request.inner.offer.amount(),
+ InvoiceContents::ForRefund { .. } => None,
+ }
+ }
+
fn description(&self) -> PrintableString {
match self {
InvoiceContents::ForOffer { invoice_request, .. } => {
}
}
+ fn offer_features(&self) -> Option<&OfferFeatures> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => {
+ Some(invoice_request.inner.offer.features())
+ },
+ InvoiceContents::ForRefund { .. } => None,
+ }
+ }
+
+ fn absolute_expiry(&self) -> Option<Duration> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => {
+ invoice_request.inner.offer.absolute_expiry()
+ },
+ InvoiceContents::ForRefund { refund, .. } => refund.absolute_expiry(),
+ }
+ }
+
+ fn issuer(&self) -> Option<PrintableString> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => {
+ invoice_request.inner.offer.issuer()
+ },
+ InvoiceContents::ForRefund { refund, .. } => refund.issuer(),
+ }
+ }
+
+ fn message_paths(&self) -> &[BlindedPath] {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => {
+ invoice_request.inner.offer.paths()
+ },
+ InvoiceContents::ForRefund { refund, .. } => refund.paths(),
+ }
+ }
+
+ fn supported_quantity(&self) -> Option<Quantity> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => {
+ Some(invoice_request.inner.offer.supported_quantity())
+ },
+ InvoiceContents::ForRefund { .. } => None,
+ }
+ }
+
+ fn payer_metadata(&self) -> &[u8] {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.metadata(),
+ InvoiceContents::ForRefund { refund, .. } => refund.metadata(),
+ }
+ }
+
+ fn invoice_request_features(&self) -> &InvoiceRequestFeatures {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.features(),
+ InvoiceContents::ForRefund { refund, .. } => refund.features(),
+ }
+ }
+
+ fn quantity(&self) -> Option<u64> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.quantity(),
+ InvoiceContents::ForRefund { refund, .. } => refund.quantity(),
+ }
+ }
+
+ fn payer_id(&self) -> PublicKey {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.payer_id(),
+ InvoiceContents::ForRefund { refund, .. } => refund.payer_id(),
+ }
+ }
+
+ fn payer_note(&self) -> Option<PrintableString> {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.payer_note(),
+ InvoiceContents::ForRefund { refund, .. } => refund.payer_note(),
+ }
+ }
+
+ fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] {
+ &self.fields().payment_paths[..]
+ }
+
+ fn created_at(&self) -> Duration {
+ self.fields().created_at
+ }
+
+ fn relative_expiry(&self) -> Duration {
+ self.fields().relative_expiry.unwrap_or(DEFAULT_RELATIVE_EXPIRY)
+ }
+
+ #[cfg(feature = "std")]
+ fn is_expired(&self) -> bool {
+ let absolute_expiry = self.created_at().checked_add(self.relative_expiry());
+ match absolute_expiry {
+ Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
+ Ok(elapsed) => elapsed > seconds_from_epoch,
+ Err(_) => false,
+ },
+ None => false,
+ }
+ }
+
+ fn payment_hash(&self) -> PaymentHash {
+ self.fields().payment_hash
+ }
+
+ fn amount_msats(&self) -> u64 {
+ self.fields().amount_msats
+ }
+
+ fn fallbacks(&self) -> Vec<Address> {
+ let chain = self.chain();
+ let network = if chain == ChainHash::using_genesis_block(Network::Bitcoin) {
+ Network::Bitcoin
+ } else if chain == ChainHash::using_genesis_block(Network::Testnet) {
+ Network::Testnet
+ } else if chain == ChainHash::using_genesis_block(Network::Signet) {
+ Network::Signet
+ } else if chain == ChainHash::using_genesis_block(Network::Regtest) {
+ Network::Regtest
+ } else {
+ return Vec::new()
+ };
+
+ let to_valid_address = |address: &FallbackAddress| {
+ let version = match WitnessVersion::try_from(address.version) {
+ Ok(version) => version,
+ Err(_) => return None,
+ };
+
+ let program = &address.program;
+ if program.len() < 2 || program.len() > 40 {
+ return None;
+ }
+
+ let address = Address {
+ payload: Payload::WitnessProgram {
+ version,
+ program: program.clone(),
+ },
+ network,
+ };
+
+ if !address.is_standard() && version == WitnessVersion::V0 {
+ return None;
+ }
+
+ Some(address)
+ };
+
+ self.fields().fallbacks
+ .as_ref()
+ .map(|fallbacks| fallbacks.iter().filter_map(to_valid_address).collect())
+ .unwrap_or_else(Vec::new)
+ }
+
+ fn features(&self) -> &Bolt12InvoiceFeatures {
+ &self.fields().features
+ }
+
+ fn signing_pubkey(&self) -> PublicKey {
+ self.fields().signing_pubkey
+ }
+
fn fields(&self) -> &InvoiceFields {
match self {
InvoiceContents::ForOffer { fields, .. } => fields,
}
}
+impl Writeable for UnsignedBolt12Invoice {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ WithoutLength(&self.bytes).write(writer)
+ }
+}
+
impl Writeable for Bolt12Invoice {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
WithoutLength(&self.bytes).write(writer)
}
}
+impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
+ type Error = Bolt12ParseError;
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ let invoice = ParsedMessage::<PartialInvoiceTlvStream>::try_from(bytes)?;
+ let ParsedMessage { bytes, tlv_stream } = invoice;
+ let (
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
+ ) = tlv_stream;
+ let contents = InvoiceContents::try_from(
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
+ )?;
+
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Ok(UnsignedBolt12Invoice { bytes, contents, tagged_hash })
+ }
+}
+
impl TryFrom<Vec<u8>> for Bolt12Invoice {
type Error = Bolt12ParseError;
InvoiceTlvStreamRef<'a>,
);
+impl SeekReadable for PartialInvoiceTlvStream {
+ fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
+ let payer = SeekReadable::read(r)?;
+ let offer = SeekReadable::read(r)?;
+ let invoice_request = SeekReadable::read(r)?;
+ let invoice = SeekReadable::read(r)?;
+
+ Ok((payer, offer, invoice_request, invoice))
+ }
+}
+
impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
type Error = Bolt12ParseError;
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
Some(signature) => signature,
};
+ let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
let pubkey = contents.fields().signing_pubkey;
- merkle::verify_signature(&signature, SIGNATURE_TAG, &bytes, pubkey)?;
+ merkle::verify_signature(&signature, message, pubkey)?;
Ok(Bolt12Invoice { bytes, contents, signature })
}
#[cfg(test)]
mod tests {
- use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG};
+ use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::sign::KeyMaterial;
- use crate::ln::features::Bolt12InvoiceFeatures;
+ use crate::ln::features::{Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::DecodeError;
use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
- use crate::offers::merkle::{SignError, SignatureTlvStreamRef, self};
- use crate::offers::offer::{OfferBuilder, OfferTlvStreamRef, Quantity};
+ use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
+ use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
use crate::offers::payer::PayerTlvStreamRef;
use crate::offers::refund::RefundBuilder;
let payment_paths = payment_paths();
let payment_hash = payment_hash();
let now = now();
- let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let unsigned_invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap()
.respond_with_no_std(payment_paths.clone(), payment_hash, now).unwrap()
- .build().unwrap()
- .sign(recipient_sign).unwrap();
+ .build().unwrap();
+
+ let mut buffer = Vec::new();
+ unsigned_invoice.write(&mut buffer).unwrap();
+
+ assert_eq!(unsigned_invoice.bytes, buffer.as_slice());
+ assert_eq!(unsigned_invoice.payer_metadata(), &[1; 32]);
+ assert_eq!(unsigned_invoice.offer_chains(), Some(vec![ChainHash::using_genesis_block(Network::Bitcoin)]));
+ assert_eq!(unsigned_invoice.metadata(), None);
+ assert_eq!(unsigned_invoice.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
+ assert_eq!(unsigned_invoice.description(), PrintableString("foo"));
+ assert_eq!(unsigned_invoice.offer_features(), Some(&OfferFeatures::empty()));
+ assert_eq!(unsigned_invoice.absolute_expiry(), None);
+ assert_eq!(unsigned_invoice.message_paths(), &[]);
+ assert_eq!(unsigned_invoice.issuer(), None);
+ assert_eq!(unsigned_invoice.supported_quantity(), Some(Quantity::One));
+ assert_eq!(unsigned_invoice.signing_pubkey(), recipient_pubkey());
+ assert_eq!(unsigned_invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+ assert_eq!(unsigned_invoice.amount_msats(), 1000);
+ assert_eq!(unsigned_invoice.invoice_request_features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(unsigned_invoice.quantity(), None);
+ assert_eq!(unsigned_invoice.payer_id(), payer_pubkey());
+ assert_eq!(unsigned_invoice.payer_note(), None);
+ assert_eq!(unsigned_invoice.payment_paths(), payment_paths.as_slice());
+ assert_eq!(unsigned_invoice.created_at(), now);
+ assert_eq!(unsigned_invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
+ #[cfg(feature = "std")]
+ assert!(!unsigned_invoice.is_expired());
+ assert_eq!(unsigned_invoice.payment_hash(), payment_hash);
+ assert_eq!(unsigned_invoice.amount_msats(), 1000);
+ assert_eq!(unsigned_invoice.fallbacks(), vec![]);
+ assert_eq!(unsigned_invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
+ assert_eq!(unsigned_invoice.signing_pubkey(), recipient_pubkey());
+
+ match UnsignedBolt12Invoice::try_from(buffer) {
+ Err(e) => panic!("error parsing unsigned invoice: {:?}", e),
+ Ok(parsed) => {
+ assert_eq!(parsed.bytes, unsigned_invoice.bytes);
+ assert_eq!(parsed.tagged_hash, unsigned_invoice.tagged_hash);
+ },
+ }
+
+ let invoice = unsigned_invoice.sign(recipient_sign).unwrap();
let mut buffer = Vec::new();
invoice.write(&mut buffer).unwrap();
assert_eq!(invoice.bytes, buffer.as_slice());
+ assert_eq!(invoice.payer_metadata(), &[1; 32]);
+ assert_eq!(invoice.offer_chains(), Some(vec![ChainHash::using_genesis_block(Network::Bitcoin)]));
+ assert_eq!(invoice.metadata(), None);
+ assert_eq!(invoice.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
assert_eq!(invoice.description(), PrintableString("foo"));
+ assert_eq!(invoice.offer_features(), Some(&OfferFeatures::empty()));
+ assert_eq!(invoice.absolute_expiry(), None);
+ assert_eq!(invoice.message_paths(), &[]);
+ assert_eq!(invoice.issuer(), None);
+ assert_eq!(invoice.supported_quantity(), Some(Quantity::One));
+ assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
+ assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+ assert_eq!(invoice.amount_msats(), 1000);
+ assert_eq!(invoice.invoice_request_features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice.quantity(), None);
+ assert_eq!(invoice.payer_id(), payer_pubkey());
+ assert_eq!(invoice.payer_note(), None);
assert_eq!(invoice.payment_paths(), payment_paths.as_slice());
assert_eq!(invoice.created_at(), now);
assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
assert_eq!(invoice.payment_hash(), payment_hash);
assert_eq!(invoice.amount_msats(), 1000);
assert_eq!(invoice.fallbacks(), vec![]);
- assert_eq!(invoice.features(), &Bolt12InvoiceFeatures::empty());
+ assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
- assert!(
- merkle::verify_signature(
- &invoice.signature, SIGNATURE_TAG, &invoice.bytes, recipient_pubkey()
- ).is_ok()
- );
+
+ let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
+ assert!(merkle::verify_signature(&invoice.signature, message, recipient_pubkey()).is_ok());
let digest = Message::from_slice(&invoice.signable_hash()).unwrap();
let pubkey = recipient_pubkey().into();
invoice.write(&mut buffer).unwrap();
assert_eq!(invoice.bytes, buffer.as_slice());
+ assert_eq!(invoice.payer_metadata(), &[1; 32]);
+ assert_eq!(invoice.offer_chains(), None);
+ assert_eq!(invoice.metadata(), None);
+ assert_eq!(invoice.amount(), None);
assert_eq!(invoice.description(), PrintableString("foo"));
+ assert_eq!(invoice.offer_features(), None);
+ assert_eq!(invoice.absolute_expiry(), None);
+ assert_eq!(invoice.message_paths(), &[]);
+ assert_eq!(invoice.issuer(), None);
+ assert_eq!(invoice.supported_quantity(), None);
+ assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
+ assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+ assert_eq!(invoice.amount_msats(), 1000);
+ assert_eq!(invoice.invoice_request_features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice.quantity(), None);
+ assert_eq!(invoice.payer_id(), payer_pubkey());
+ assert_eq!(invoice.payer_note(), None);
assert_eq!(invoice.payment_paths(), payment_paths.as_slice());
assert_eq!(invoice.created_at(), now);
assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
assert_eq!(invoice.payment_hash(), payment_hash);
assert_eq!(invoice.amount_msats(), 1000);
assert_eq!(invoice.fallbacks(), vec![]);
- assert_eq!(invoice.features(), &Bolt12InvoiceFeatures::empty());
+ assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
- assert!(
- merkle::verify_signature(
- &invoice.signature, SIGNATURE_TAG, &invoice.bytes, recipient_pubkey()
- ).is_ok()
- );
+
+ let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
+ assert!(merkle::verify_signature(&invoice.signature, message, recipient_pubkey()).is_ok());
assert_eq!(
invoice.as_tlv_stream(),
.build().unwrap()
.sign(recipient_sign).unwrap();
let (_, _, _, tlv_stream, _) = invoice.as_tlv_stream();
- assert_eq!(invoice.features(), &features);
+ assert_eq!(invoice.invoice_features(), &features);
assert_eq!(tlv_stream.features, Some(&features));
}
Ok(invoice) => {
let mut features = Bolt12InvoiceFeatures::empty();
features.set_basic_mpp_optional();
- assert_eq!(invoice.features(), &features);
+ assert_eq!(invoice.invoice_features(), &features);
},
Err(e) => panic!("error parsing invoice: {:?}", e),
}
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
- let mut unsigned_invoice = invoice_request
+ let mut invoice_builder = invoice_request
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
.fallback_v0_p2wsh(&script.wscript_hash())
.fallback_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap())
- .fallback_v1_p2tr_tweaked(&tweaked_pubkey)
- .build().unwrap();
+ .fallback_v1_p2tr_tweaked(&tweaked_pubkey);
// Only standard addresses will be included.
- let fallbacks = unsigned_invoice.invoice.fields_mut().fallbacks.as_mut().unwrap();
+ let fallbacks = invoice_builder.invoice.fields_mut().fallbacks.as_mut().unwrap();
// Non-standard addresses
fallbacks.push(FallbackAddress { version: 1, program: vec![0u8; 41] });
fallbacks.push(FallbackAddress { version: 2, program: vec![0u8; 1] });
fallbacks.push(FallbackAddress { version: 1, program: vec![0u8; 33] });
fallbacks.push(FallbackAddress { version: 2, program: vec![0u8; 40] });
- let invoice = unsigned_invoice.sign(recipient_sign).unwrap();
+ let invoice = invoice_builder.build().unwrap().sign(recipient_sign).unwrap();
let mut buffer = Vec::new();
invoice.write(&mut buffer).unwrap();
.sign(payer_sign).unwrap()
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
.build().unwrap()
- .invoice
+ .contents
.write(&mut buffer).unwrap();
match Bolt12Invoice::try_from(buffer) {
//! .quantity(5)?
//! .payer_note("foo".to_string())
//! .build()?
-//! .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+//! .sign::<_, Infallible>(
+//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//! )
//! .expect("failed verifying signature")
//! .write(&mut buffer)
//! .unwrap();
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
-use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, self};
+use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::{Infallible, TryFrom};
+use core::convert::{AsRef, Infallible, TryFrom};
use core::ops::Deref;
use crate::sign::EntropySource;
use crate::io;
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
use crate::ln::msgs::DecodeError;
use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
-use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self};
+use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
use crate::prelude::*;
-const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature");
+/// Tag for the hash function used when signing an [`InvoiceRequest`]'s merkle root.
+pub const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature");
pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Invreq ~~~~~";
}
fn build_with_checks(mut self) -> Result<
- (UnsignedInvoiceRequest<'a>, Option<KeyPair>, Option<&'b Secp256k1<T>>),
+ (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<T>>),
Bolt12SemanticError
> {
#[cfg(feature = "std")] {
}
fn build_without_checks(mut self) ->
- (UnsignedInvoiceRequest<'a>, Option<KeyPair>, Option<&'b Secp256k1<T>>)
+ (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<T>>)
{
// Create the metadata for stateless verification of a Bolt12Invoice.
let mut keys = None;
debug_assert!(self.payer_id.is_some());
let payer_id = self.payer_id.unwrap();
- let unsigned_invoice = UnsignedInvoiceRequest {
- offer: self.offer,
- invoice_request: InvoiceRequestContents {
- inner: self.invoice_request,
- payer_id,
- },
+ let invoice_request = InvoiceRequestContents {
+ inner: self.invoice_request,
+ payer_id,
};
+ let unsigned_invoice_request = UnsignedInvoiceRequest::new(self.offer, invoice_request);
- (unsigned_invoice, keys, secp_ctx)
+ (unsigned_invoice_request, keys, secp_ctx)
}
}
impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> {
/// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
/// by [`UnsignedInvoiceRequest::sign`].
- pub fn build(self) -> Result<UnsignedInvoiceRequest<'a>, Bolt12SemanticError> {
+ pub fn build(self) -> Result<UnsignedInvoiceRequest, Bolt12SemanticError> {
let (unsigned_invoice_request, keys, _) = self.build_with_checks()?;
debug_assert!(keys.is_none());
Ok(unsigned_invoice_request)
let secp_ctx = secp_ctx.unwrap();
let keys = keys.unwrap();
let invoice_request = unsigned_invoice_request
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+ )
.unwrap();
Ok(invoice_request)
}
self
}
- pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> {
+ pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest {
self.build_without_checks().0
}
}
/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
-pub struct UnsignedInvoiceRequest<'a> {
- offer: &'a Offer,
- invoice_request: InvoiceRequestContents,
+///
+/// # Serialization
+///
+/// This is serialized as a TLV stream, which includes TLV records from the originating message. As
+/// such, it may include unknown, odd TLV records.
+pub struct UnsignedInvoiceRequest {
+ bytes: Vec<u8>,
+ contents: InvoiceRequestContents,
+ tagged_hash: TaggedHash,
}
-impl<'a> UnsignedInvoiceRequest<'a> {
- /// Signs the invoice request using the given function.
- ///
- /// This is not exported to bindings users as functions are not yet mapped.
- pub fn sign<F, E>(self, sign: F) -> Result<InvoiceRequest, SignError<E>>
- where
- F: FnOnce(&Message) -> Result<Signature, E>
- {
+impl UnsignedInvoiceRequest {
+ fn new(offer: &Offer, contents: InvoiceRequestContents) -> Self {
// Use the offer bytes instead of the offer TLV stream as the offer may have contained
// unknown TLV records, which are not stored in `OfferContents`.
let (payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
- self.invoice_request.as_tlv_stream();
- let offer_bytes = WithoutLength(&self.offer.bytes);
+ contents.as_tlv_stream();
+ let offer_bytes = WithoutLength(&offer.bytes);
let unsigned_tlv_stream = (payer_tlv_stream, offer_bytes, invoice_request_tlv_stream);
let mut bytes = Vec::new();
unsigned_tlv_stream.write(&mut bytes).unwrap();
- let pubkey = self.invoice_request.payer_id;
- let signature = merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?;
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Self { bytes, contents, tagged_hash }
+ }
+
+ /// Returns the [`TaggedHash`] of the invoice to sign.
+ pub fn tagged_hash(&self) -> &TaggedHash {
+ &self.tagged_hash
+ }
+
+ /// Signs the [`TaggedHash`] of the invoice request using the given function.
+ ///
+ /// Note: The hash computation may have included unknown, odd TLV records.
+ ///
+ /// This is not exported to bindings users as functions are not yet mapped.
+ pub fn sign<F, E>(mut self, sign: F) -> Result<InvoiceRequest, SignError<E>>
+ where
+ F: FnOnce(&Self) -> Result<Signature, E>
+ {
+ let pubkey = self.contents.payer_id;
+ let signature = merkle::sign_message(sign, &self, pubkey)?;
// Append the signature TLV record to the bytes.
let signature_tlv_stream = SignatureTlvStreamRef {
signature: Some(&signature),
};
- signature_tlv_stream.write(&mut bytes).unwrap();
+ signature_tlv_stream.write(&mut self.bytes).unwrap();
Ok(InvoiceRequest {
- bytes,
- contents: self.invoice_request,
+ bytes: self.bytes,
+ contents: self.contents,
signature,
})
}
}
+impl AsRef<TaggedHash> for UnsignedInvoiceRequest {
+ fn as_ref(&self) -> &TaggedHash {
+ &self.tagged_hash
+ }
+}
+
/// An `InvoiceRequest` is a request for a [`Bolt12Invoice`] formulated from an [`Offer`].
///
/// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request
payer_note: Option<String>,
}
-impl InvoiceRequest {
+macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => {
/// An unpredictable series of bytes, typically containing information about the derivation of
/// [`payer_id`].
///
/// [`payer_id`]: Self::payer_id
- pub fn metadata(&self) -> &[u8] {
- self.contents.metadata()
+ pub fn payer_metadata(&$self) -> &[u8] {
+ $contents.metadata()
}
/// A chain from [`Offer::chains`] that the offer is valid for.
- pub fn chain(&self) -> ChainHash {
- self.contents.chain()
+ pub fn chain(&$self) -> ChainHash {
+ $contents.chain()
}
/// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
/// must be greater than or equal to [`Offer::amount`], converted if necessary.
///
/// [`chain`]: Self::chain
- pub fn amount_msats(&self) -> Option<u64> {
- self.contents.inner.amount_msats
+ pub fn amount_msats(&$self) -> Option<u64> {
+ $contents.amount_msats()
}
/// Features pertaining to requesting an invoice.
- pub fn features(&self) -> &InvoiceRequestFeatures {
- &self.contents.inner.features
+ pub fn invoice_request_features(&$self) -> &InvoiceRequestFeatures {
+ &$contents.features()
}
/// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
- pub fn quantity(&self) -> Option<u64> {
- self.contents.inner.quantity
+ pub fn quantity(&$self) -> Option<u64> {
+ $contents.quantity()
}
/// A possibly transient pubkey used to sign the invoice request.
- pub fn payer_id(&self) -> PublicKey {
- self.contents.payer_id
+ pub fn payer_id(&$self) -> PublicKey {
+ $contents.payer_id()
}
/// A payer-provided note which will be seen by the recipient and reflected back in the invoice
/// response.
- pub fn payer_note(&self) -> Option<PrintableString> {
- self.contents.inner.payer_note.as_ref()
- .map(|payer_note| PrintableString(payer_note.as_str()))
+ pub fn payer_note(&$self) -> Option<PrintableString> {
+ $contents.payer_note()
}
+} }
+
+impl UnsignedInvoiceRequest {
+ offer_accessors!(self, self.contents.inner.offer);
+ invoice_request_accessors!(self, self.contents);
+}
+
+impl InvoiceRequest {
+ offer_accessors!(self, self.contents.inner.offer);
+ invoice_request_accessors!(self, self.contents);
/// Signature of the invoice request using [`payer_id`].
///
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
created_at: core::time::Duration
) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
- if self.features().requires_unknown_bits() {
+ if self.invoice_request_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
- if self.features().requires_unknown_bits() {
+ if self.invoice_request_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
}
impl InvoiceRequestContents {
- pub fn metadata(&self) -> &[u8] {
+ pub(super) fn metadata(&self) -> &[u8] {
self.inner.metadata()
}
self.inner.chain()
}
+ pub(super) fn amount_msats(&self) -> Option<u64> {
+ self.inner.amount_msats
+ }
+
+ pub(super) fn features(&self) -> &InvoiceRequestFeatures {
+ &self.inner.features
+ }
+
+ pub(super) fn quantity(&self) -> Option<u64> {
+ self.inner.quantity
+ }
+
pub(super) fn payer_id(&self) -> PublicKey {
self.payer_id
}
+ pub(super) fn payer_note(&self) -> Option<PrintableString> {
+ self.inner.payer_note.as_ref()
+ .map(|payer_note| PrintableString(payer_note.as_str()))
+ }
+
pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
let (payer, offer, mut invoice_request) = self.inner.as_tlv_stream();
invoice_request.payer_id = Some(&self.payer_id);
}
}
+impl Writeable for UnsignedInvoiceRequest {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ WithoutLength(&self.bytes).write(writer)
+ }
+}
+
impl Writeable for InvoiceRequest {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
WithoutLength(&self.bytes).write(writer)
InvoiceRequestTlvStreamRef<'a>,
);
+impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
+ type Error = Bolt12ParseError;
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ let invoice_request = ParsedMessage::<PartialInvoiceRequestTlvStream>::try_from(bytes)?;
+ let ParsedMessage { bytes, tlv_stream } = invoice_request;
+ let (
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+ ) = tlv_stream;
+ let contents = InvoiceRequestContents::try_from(
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+ )?;
+
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
+ }
+}
+
impl TryFrom<Vec<u8>> for InvoiceRequest {
type Error = Bolt12ParseError;
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
Some(signature) => signature,
};
- merkle::verify_signature(&signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
+ let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
+ merkle::verify_signature(&signature, message, contents.payer_id)?;
Ok(InvoiceRequest { bytes, contents, signature })
}
#[cfg(test)]
mod tests {
- use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG};
+ use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
#[cfg(feature = "std")]
use core::time::Duration;
use crate::sign::KeyMaterial;
- use crate::ln::features::InvoiceRequestFeatures;
+ use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures};
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG};
- use crate::offers::merkle::{SignError, SignatureTlvStreamRef, self};
+ use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
use crate::offers::payer::PayerTlvStreamRef;
#[test]
fn builds_invoice_request_with_defaults() {
- let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
- .build().unwrap()
- .sign(payer_sign).unwrap();
+ .build().unwrap();
+
+ let mut buffer = Vec::new();
+ unsigned_invoice_request.write(&mut buffer).unwrap();
+
+ assert_eq!(unsigned_invoice_request.bytes, buffer.as_slice());
+ assert_eq!(unsigned_invoice_request.payer_metadata(), &[1; 32]);
+ assert_eq!(unsigned_invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+ assert_eq!(unsigned_invoice_request.metadata(), None);
+ assert_eq!(unsigned_invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
+ assert_eq!(unsigned_invoice_request.description(), PrintableString("foo"));
+ assert_eq!(unsigned_invoice_request.offer_features(), &OfferFeatures::empty());
+ assert_eq!(unsigned_invoice_request.absolute_expiry(), None);
+ assert_eq!(unsigned_invoice_request.paths(), &[]);
+ assert_eq!(unsigned_invoice_request.issuer(), None);
+ assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One);
+ assert_eq!(unsigned_invoice_request.signing_pubkey(), recipient_pubkey());
+ assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+ assert_eq!(unsigned_invoice_request.amount_msats(), None);
+ assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(unsigned_invoice_request.quantity(), None);
+ assert_eq!(unsigned_invoice_request.payer_id(), payer_pubkey());
+ assert_eq!(unsigned_invoice_request.payer_note(), None);
+
+ match UnsignedInvoiceRequest::try_from(buffer) {
+ Err(e) => panic!("error parsing unsigned invoice request: {:?}", e),
+ Ok(parsed) => {
+ assert_eq!(parsed.bytes, unsigned_invoice_request.bytes);
+ assert_eq!(parsed.tagged_hash, unsigned_invoice_request.tagged_hash);
+ },
+ }
+
+ let invoice_request = unsigned_invoice_request.sign(payer_sign).unwrap();
let mut buffer = Vec::new();
invoice_request.write(&mut buffer).unwrap();
assert_eq!(invoice_request.bytes, buffer.as_slice());
- assert_eq!(invoice_request.metadata(), &[1; 32]);
+ assert_eq!(invoice_request.payer_metadata(), &[1; 32]);
+ assert_eq!(invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+ assert_eq!(invoice_request.metadata(), None);
+ assert_eq!(invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
+ assert_eq!(invoice_request.description(), PrintableString("foo"));
+ assert_eq!(invoice_request.offer_features(), &OfferFeatures::empty());
+ assert_eq!(invoice_request.absolute_expiry(), None);
+ assert_eq!(invoice_request.paths(), &[]);
+ assert_eq!(invoice_request.issuer(), None);
+ assert_eq!(invoice_request.supported_quantity(), Quantity::One);
+ assert_eq!(invoice_request.signing_pubkey(), recipient_pubkey());
assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
assert_eq!(invoice_request.amount_msats(), None);
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
assert_eq!(invoice_request.quantity(), None);
assert_eq!(invoice_request.payer_id(), payer_pubkey());
assert_eq!(invoice_request.payer_note(), None);
- assert!(
- merkle::verify_signature(
- &invoice_request.signature, SIGNATURE_TAG, &invoice_request.bytes, payer_pubkey()
- ).is_ok()
- );
+
+ let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes);
+ assert!(merkle::verify_signature(&invoice_request.signature, message, payer_pubkey()).is_ok());
assert_eq!(
invoice_request.as_tlv_stream(),
let mut bytes = Vec::new();
tlv_stream.write(&mut bytes).unwrap();
- let signature = merkle::sign_message(
- recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey()
- ).unwrap();
+ let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+ let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
signature_tlv_stream.signature = Some(&signature);
let mut encoded_invoice = bytes;
let mut bytes = Vec::new();
tlv_stream.write(&mut bytes).unwrap();
- let signature = merkle::sign_message(
- recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey()
- ).unwrap();
+ let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+ let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
signature_tlv_stream.signature = Some(&signature);
let mut encoded_invoice = bytes;
let mut bytes = Vec::new();
tlv_stream.write(&mut bytes).unwrap();
- let signature = merkle::sign_message(
- recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey()
- ).unwrap();
+ let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+ let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
signature_tlv_stream.signature = Some(&signature);
let mut encoded_invoice = bytes;
let mut bytes = Vec::new();
tlv_stream.write(&mut bytes).unwrap();
- let signature = merkle::sign_message(
- recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey()
- ).unwrap();
+ let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+ let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
signature_tlv_stream.signature = Some(&signature);
let mut encoded_invoice = bytes;
.build().unwrap()
.sign(payer_sign).unwrap();
let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::unknown());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::unknown());
assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown()));
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.build().unwrap()
.sign(payer_sign).unwrap();
let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
assert_eq!(tlv_stream.features, None);
}
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.0.metadata = None;
let mut buffer = Vec::new();
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.2.payer_id = None;
let mut buffer = Vec::new();
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.1.node_id = None;
let mut buffer = Vec::new();
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
- .invoice_request
+ .contents
.write(&mut buffer).unwrap();
match InvoiceRequest::try_from(buffer) {
.build().unwrap()
.request_invoice(vec![1; 32], keys.public_key()).unwrap()
.build().unwrap()
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+ )
.unwrap();
let mut encoded_invoice_request = Vec::new();
use bitcoin::hashes::{Hash, HashEngine, sha256};
use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, self};
use bitcoin::secp256k1::schnorr::Signature;
+use core::convert::AsRef;
use crate::io;
use crate::util::ser::{BigSize, Readable, Writeable, Writer};
(240, signature: Signature),
});
+/// A hash for use in a specific context by tweaking with a context-dependent tag as per [BIP 340]
+/// and computed over the merkle root of a TLV stream to sign as defined in [BOLT 12].
+///
+/// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
+/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
+#[derive(Debug, PartialEq)]
+pub struct TaggedHash(Message);
+
+impl TaggedHash {
+ /// Creates a tagged hash with the given parameters.
+ ///
+ /// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
+ pub(super) fn new(tag: &str, tlv_stream: &[u8]) -> Self {
+ Self(message_digest(tag, tlv_stream))
+ }
+
+ /// Returns the digest to sign.
+ pub fn as_digest(&self) -> &Message {
+ &self.0
+ }
+}
+
+impl AsRef<TaggedHash> for TaggedHash {
+ fn as_ref(&self) -> &TaggedHash {
+ self
+ }
+}
+
/// Error when signing messages.
#[derive(Debug, PartialEq)]
pub enum SignError<E> {
Verification(secp256k1::Error),
}
-/// Signs a message digest consisting of a tagged hash of the given bytes, checking if it can be
-/// verified with the supplied pubkey.
+/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
+/// can be verified with the supplied `pubkey`.
+///
+/// Since `message` is any type that implements [`AsRef<TaggedHash>`], `sign` may be a closure that
+/// takes a message such as [`Bolt12Invoice`] or [`InvoiceRequest`]. This allows further message
+/// verification before signing its [`TaggedHash`].
///
-/// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
-pub(super) fn sign_message<F, E>(
- sign: F, tag: &str, bytes: &[u8], pubkey: PublicKey,
+/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+pub(super) fn sign_message<F, E, T>(
+ sign: F, message: &T, pubkey: PublicKey,
) -> Result<Signature, SignError<E>>
where
- F: FnOnce(&Message) -> Result<Signature, E>
+ F: FnOnce(&T) -> Result<Signature, E>,
+ T: AsRef<TaggedHash>,
{
- let digest = message_digest(tag, bytes);
- let signature = sign(&digest).map_err(|e| SignError::Signing(e))?;
+ let signature = sign(message).map_err(|e| SignError::Signing(e))?;
+ let digest = message.as_ref().as_digest();
let pubkey = pubkey.into();
let secp_ctx = Secp256k1::verification_only();
- secp_ctx.verify_schnorr(&signature, &digest, &pubkey).map_err(|e| SignError::Verification(e))?;
+ secp_ctx.verify_schnorr(&signature, digest, &pubkey).map_err(|e| SignError::Verification(e))?;
Ok(signature)
}
-/// Verifies the signature with a pubkey over the given bytes using a tagged hash as the message
+/// Verifies the signature with a pubkey over the given message using a tagged hash as the message
/// digest.
-///
-/// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
pub(super) fn verify_signature(
- signature: &Signature, tag: &str, bytes: &[u8], pubkey: PublicKey,
+ signature: &Signature, message: TaggedHash, pubkey: PublicKey,
) -> Result<(), secp256k1::Error> {
- let digest = message_digest(tag, bytes);
+ let digest = message.as_digest();
let pubkey = pubkey.into();
let secp_ctx = Secp256k1::verification_only();
- secp_ctx.verify_schnorr(signature, &digest, &pubkey)
+ secp_ctx.verify_schnorr(signature, digest, &pubkey)
}
pub(super) fn message_digest(tag: &str, bytes: &[u8]) -> Message {
/// Encoding for a pre-serialized TLV stream that excludes any signature TLV records.
///
/// Panics if the wrapped bytes are not a well-formed TLV stream.
-pub(super) struct WithoutSignatures<'a>(pub &'a Vec<u8>);
+pub(super) struct WithoutSignatures<'a>(pub &'a [u8]);
impl<'a> Writeable for WithoutSignatures<'a> {
#[inline]
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
- let tlv_stream = TlvStream::new(&self.0[..]);
+ let tlv_stream = TlvStream::new(self.0);
for record in tlv_stream.skip_signatures() {
writer.write_all(record.record_bytes)?;
}
.build_unchecked()
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
.build_unchecked()
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &payer_keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+ )
.unwrap();
assert_eq!(
invoice_request.to_string(),
.build_unchecked()
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
.build_unchecked()
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &payer_keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+ )
.unwrap();
let mut bytes_without_signature = Vec::new();
.build_unchecked()
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
.build_unchecked()
- .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &payer_keys)))
+ .sign::<_, Infallible>(
+ |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+ )
.unwrap();
let tlv_stream = TlvStream::new(&invoice_request.bytes).range(0..1)
//!
//! Offers are a flexible protocol for Lightning payments.
+#[macro_use]
+pub mod offer;
+
pub mod invoice;
pub mod invoice_error;
pub mod invoice_request;
-mod merkle;
-pub mod offer;
+pub mod merkle;
pub mod parse;
mod payer;
pub mod refund;
signing_pubkey: PublicKey,
}
-impl Offer {
+macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
// TODO: Return a slice once ChainHash has constants.
// - https://github.com/rust-bitcoin/rust-bitcoin/pull/1283
// - https://github.com/rust-bitcoin/rust-bitcoin/pull/1286
/// The chains that may be used when paying a requested invoice (e.g., bitcoin mainnet).
/// Payments must be denominated in units of the minimal lightning-payable unit (e.g., msats)
/// for the selected chain.
- pub fn chains(&self) -> Vec<ChainHash> {
- self.contents.chains()
- }
-
- pub(super) fn implied_chain(&self) -> ChainHash {
- self.contents.implied_chain()
- }
-
- /// Returns whether the given chain is supported by the offer.
- pub fn supports_chain(&self, chain: ChainHash) -> bool {
- self.contents.supports_chain(chain)
+ pub fn chains(&$self) -> Vec<$crate::bitcoin::blockdata::constants::ChainHash> {
+ $contents.chains()
}
// TODO: Link to corresponding method in `InvoiceRequest`.
/// Opaque bytes set by the originator. Useful for authentication and validating fields since it
/// is reflected in `invoice_request` messages along with all the other fields from the `offer`.
- pub fn metadata(&self) -> Option<&Vec<u8>> {
- self.contents.metadata()
+ pub fn metadata(&$self) -> Option<&Vec<u8>> {
+ $contents.metadata()
}
/// The minimum amount required for a successful payment of a single item.
- pub fn amount(&self) -> Option<&Amount> {
- self.contents.amount()
+ pub fn amount(&$self) -> Option<&$crate::offers::offer::Amount> {
+ $contents.amount()
}
/// A complete description of the purpose of the payment. Intended to be displayed to the user
/// but with the caveat that it has not been verified in any way.
- pub fn description(&self) -> PrintableString {
- self.contents.description()
+ pub fn description(&$self) -> $crate::util::string::PrintableString {
+ $contents.description()
}
/// Features pertaining to the offer.
- pub fn features(&self) -> &OfferFeatures {
- &self.contents.features
+ pub fn offer_features(&$self) -> &$crate::ln::features::OfferFeatures {
+ &$contents.features()
}
/// Duration since the Unix epoch when an invoice should no longer be requested.
///
/// If `None`, the offer does not expire.
- pub fn absolute_expiry(&self) -> Option<Duration> {
- self.contents.absolute_expiry
- }
-
- /// Whether the offer has expired.
- #[cfg(feature = "std")]
- pub fn is_expired(&self) -> bool {
- self.contents.is_expired()
+ pub fn absolute_expiry(&$self) -> Option<core::time::Duration> {
+ $contents.absolute_expiry()
}
/// The issuer of the offer, possibly beginning with `user@domain` or `domain`. Intended to be
/// displayed to the user but with the caveat that it has not been verified in any way.
- pub fn issuer(&self) -> Option<PrintableString> {
- self.contents.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
+ pub fn issuer(&$self) -> Option<$crate::util::string::PrintableString> {
+ $contents.issuer()
}
/// Paths to the recipient originating from publicly reachable nodes. Blinded paths provide
/// recipient privacy by obfuscating its node id.
- pub fn paths(&self) -> &[BlindedPath] {
- self.contents.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
+ pub fn paths(&$self) -> &[$crate::blinded_path::BlindedPath] {
+ $contents.paths()
}
/// The quantity of items supported.
- pub fn supported_quantity(&self) -> Quantity {
- self.contents.supported_quantity()
+ pub fn supported_quantity(&$self) -> $crate::offers::offer::Quantity {
+ $contents.supported_quantity()
+ }
+
+ /// The public key used by the recipient to sign invoices.
+ pub fn signing_pubkey(&$self) -> $crate::bitcoin::secp256k1::PublicKey {
+ $contents.signing_pubkey()
+ }
+} }
+
+impl Offer {
+ offer_accessors!(self, self.contents);
+
+ pub(super) fn implied_chain(&self) -> ChainHash {
+ self.contents.implied_chain()
+ }
+
+ /// Returns whether the given chain is supported by the offer.
+ pub fn supports_chain(&self, chain: ChainHash) -> bool {
+ self.contents.supports_chain(chain)
+ }
+
+ /// Whether the offer has expired.
+ #[cfg(feature = "std")]
+ pub fn is_expired(&self) -> bool {
+ self.contents.is_expired()
}
/// Returns whether the given quantity is valid for the offer.
self.contents.expects_quantity()
}
- /// The public key used by the recipient to sign invoices.
- pub fn signing_pubkey(&self) -> PublicKey {
- self.contents.signing_pubkey()
- }
-
/// Similar to [`Offer::request_invoice`] except it:
/// - derives the [`InvoiceRequest::payer_id`] such that a different key can be used for each
/// request, and
- /// - sets the [`InvoiceRequest::metadata`] when [`InvoiceRequestBuilder::build`] is called such
- /// that it can be used by [`Bolt12Invoice::verify`] to determine if the invoice was requested
- /// using a base [`ExpandedKey`] from which the payer id was derived.
+ /// - sets the [`InvoiceRequest::payer_metadata`] when [`InvoiceRequestBuilder::build`] is
+ /// called such that it can be used by [`Bolt12Invoice::verify`] to determine if the invoice
+ /// was requested using a base [`ExpandedKey`] from which the payer id was derived.
///
/// Useful to protect the sender's privacy.
///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
///
/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
- /// [`InvoiceRequest::metadata`]: crate::offers::invoice_request::InvoiceRequest::metadata
+ /// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata
/// [`Bolt12Invoice::verify`]: crate::offers::invoice::Bolt12Invoice::verify
/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
pub fn request_invoice_deriving_payer_id<'a, 'b, ES: Deref, T: secp256k1::Signing>(
where
ES::Target: EntropySource,
{
- if self.features().requires_unknown_bits() {
+ if self.offer_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
where
ES::Target: EntropySource,
{
- if self.features().requires_unknown_bits() {
+ if self.offer_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
pub fn request_invoice(
&self, metadata: Vec<u8>, payer_id: PublicKey
) -> Result<InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>, Bolt12SemanticError> {
- if self.features().requires_unknown_bits() {
+ if self.offer_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
self.metadata.as_ref().and_then(|metadata| metadata.as_bytes())
}
+ pub fn amount(&self) -> Option<&Amount> {
+ self.amount.as_ref()
+ }
+
pub fn description(&self) -> PrintableString {
PrintableString(&self.description)
}
+ pub fn features(&self) -> &OfferFeatures {
+ &self.features
+ }
+
+ pub fn absolute_expiry(&self) -> Option<Duration> {
+ self.absolute_expiry
+ }
+
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
match self.absolute_expiry {
}
}
- pub fn amount(&self) -> Option<&Amount> {
- self.amount.as_ref()
+ pub fn issuer(&self) -> Option<PrintableString> {
+ self.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
+ }
+
+ pub fn paths(&self) -> &[BlindedPath] {
+ self.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
}
pub(super) fn check_amount_msats_for_quantity(
assert_eq!(offer.metadata(), None);
assert_eq!(offer.amount(), None);
assert_eq!(offer.description(), PrintableString("foo"));
- assert_eq!(offer.features(), &OfferFeatures::empty());
+ assert_eq!(offer.offer_features(), &OfferFeatures::empty());
assert_eq!(offer.absolute_expiry(), None);
#[cfg(feature = "std")]
assert!(!offer.is_expired());
.features_unchecked(OfferFeatures::unknown())
.build()
.unwrap();
- assert_eq!(offer.features(), &OfferFeatures::unknown());
+ assert_eq!(offer.offer_features(), &OfferFeatures::unknown());
assert_eq!(offer.as_tlv_stream().features, Some(&OfferFeatures::unknown()));
let offer = OfferBuilder::new("foo".into(), pubkey(42))
.features_unchecked(OfferFeatures::empty())
.build()
.unwrap();
- assert_eq!(offer.features(), &OfferFeatures::empty());
+ assert_eq!(offer.offer_features(), &OfferFeatures::empty());
assert_eq!(offer.as_tlv_stream().features, None);
}
#[cfg_attr(test, derive(PartialEq))]
pub(super) struct PayerContents(pub Metadata);
-/// TLV record type for [`InvoiceRequest::metadata`] and [`Refund::metadata`].
+/// TLV record type for [`InvoiceRequest::payer_metadata`] and [`Refund::payer_metadata`].
///
-/// [`InvoiceRequest::metadata`]: crate::offers::invoice_request::InvoiceRequest::metadata
-/// [`Refund::metadata`]: crate::offers::refund::Refund::metadata
+/// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata
+/// [`Refund::payer_metadata`]: crate::offers::refund::Refund::payer_metadata
pub(super) const PAYER_METADATA_TYPE: u64 = 0;
tlv_stream!(PayerTlvStream, PayerTlvStreamRef, 0..1, {
/// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to
/// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey.
///
- /// Additionally, sets the required [`Refund::description`], [`Refund::metadata`], and
+ /// Additionally, sets the required [`Refund::description`], [`Refund::payer_metadata`], and
/// [`Refund::amount_msats`].
pub fn new(
description: String, metadata: Vec<u8>, payer_id: PublicKey, amount_msats: u64
///
/// If `None`, the refund does not expire.
pub fn absolute_expiry(&self) -> Option<Duration> {
- self.contents.absolute_expiry
+ self.contents.absolute_expiry()
}
/// Whether the refund has expired.
/// The issuer of the refund, possibly beginning with `user@domain` or `domain`. Intended to be
/// displayed to the user but with the caveat that it has not been verified in any way.
pub fn issuer(&self) -> Option<PrintableString> {
- self.contents.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
+ self.contents.issuer()
}
/// Paths to the sender originating from publicly reachable nodes. Blinded paths provide sender
/// privacy by obfuscating its node id.
pub fn paths(&self) -> &[BlindedPath] {
- self.contents.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
+ self.contents.paths()
}
/// An unpredictable series of bytes, typically containing information about the derivation of
/// [`payer_id`].
///
/// [`payer_id`]: Self::payer_id
- pub fn metadata(&self) -> &[u8] {
+ pub fn payer_metadata(&self) -> &[u8] {
self.contents.metadata()
}
/// A chain that the refund is valid for.
pub fn chain(&self) -> ChainHash {
- self.contents.chain.unwrap_or_else(|| self.contents.implied_chain())
+ self.contents.chain()
}
/// The amount to refund in msats (i.e., the minimum lightning-payable unit for [`chain`]).
///
/// [`chain`]: Self::chain
pub fn amount_msats(&self) -> u64 {
- self.contents.amount_msats
+ self.contents.amount_msats()
}
/// Features pertaining to requesting an invoice.
pub fn features(&self) -> &InvoiceRequestFeatures {
- &self.contents.features
+ &self.contents.features()
}
/// The quantity of an item that refund is for.
pub fn quantity(&self) -> Option<u64> {
- self.contents.quantity
+ self.contents.quantity()
}
/// A public node id to send to in the case where there are no [`paths`]. Otherwise, a possibly
///
/// [`paths`]: Self::paths
pub fn payer_id(&self) -> PublicKey {
- self.contents.payer_id
+ self.contents.payer_id()
}
/// Payer provided note to include in the invoice.
pub fn payer_note(&self) -> Option<PrintableString> {
- self.contents.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
+ self.contents.payer_note()
}
/// Creates an [`InvoiceBuilder`] for the refund with the given required fields and using the
PrintableString(&self.description)
}
+ pub fn absolute_expiry(&self) -> Option<Duration> {
+ self.absolute_expiry
+ }
+
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
match self.absolute_expiry {
}
}
+ pub fn issuer(&self) -> Option<PrintableString> {
+ self.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
+ }
+
+ pub fn paths(&self) -> &[BlindedPath] {
+ self.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
+ }
+
pub(super) fn metadata(&self) -> &[u8] {
self.payer.0.as_bytes().map(|bytes| bytes.as_slice()).unwrap_or(&[])
}
ChainHash::using_genesis_block(Network::Bitcoin)
}
- pub(super) fn derives_keys(&self) -> bool {
- self.payer.0.derives_keys()
+ pub fn amount_msats(&self) -> u64 {
+ self.amount_msats
+ }
+
+ /// Features pertaining to requesting an invoice.
+ pub fn features(&self) -> &InvoiceRequestFeatures {
+ &self.features
}
- pub(super) fn payer_id(&self) -> PublicKey {
+ /// The quantity of an item that refund is for.
+ pub fn quantity(&self) -> Option<u64> {
+ self.quantity
+ }
+
+ /// A public node id to send to in the case where there are no [`paths`]. Otherwise, a possibly
+ /// transient pubkey.
+ ///
+ /// [`paths`]: Self::paths
+ pub fn payer_id(&self) -> PublicKey {
self.payer_id
}
+ /// Payer provided note to include in the invoice.
+ pub fn payer_note(&self) -> Option<PrintableString> {
+ self.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
+ }
+
+ pub(super) fn derives_keys(&self) -> bool {
+ self.payer.0.derives_keys()
+ }
+
pub(super) fn as_tlv_stream(&self) -> RefundTlvStreamRef {
let payer = PayerTlvStreamRef {
metadata: self.payer.0.as_bytes(),
refund.write(&mut buffer).unwrap();
assert_eq!(refund.bytes, buffer.as_slice());
- assert_eq!(refund.metadata(), &[1; 32]);
+ assert_eq!(refund.payer_metadata(), &[1; 32]);
assert_eq!(refund.description(), PrintableString("foo"));
assert_eq!(refund.absolute_expiry(), None);
#[cfg(feature = "std")]
//! Utilities for testing BOLT 12 Offers interfaces
-use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, SecretKey};
+use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::Infallible;
+use core::convert::{AsRef, Infallible};
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::sign::EntropySource;
use crate::ln::PaymentHash;
use crate::ln::features::BlindedHopFeatures;
use crate::offers::invoice::BlindedPayInfo;
+use crate::offers::merkle::TaggedHash;
pub(super) fn payer_keys() -> KeyPair {
let secp_ctx = Secp256k1::new();
KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
}
-pub(super) fn payer_sign(digest: &Message) -> Result<Signature, Infallible> {
+pub(super) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
let secp_ctx = Secp256k1::new();
let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
- Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
}
pub(super) fn payer_pubkey() -> PublicKey {
KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
}
-pub(super) fn recipient_sign(digest: &Message) -> Result<Signature, Infallible> {
+pub(super) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
let secp_ctx = Secp256k1::new();
let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
- Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
+ Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
}
pub(super) fn recipient_pubkey() -> PublicKey {
impl fmt::Debug for NodeId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "NodeId({})", log_bytes!(self.0))
+ write!(f, "NodeId({})", crate::util::logger::DebugBytes(&self.0))
}
}
impl fmt::Display for NodeId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", log_bytes!(self.0))
+ crate::util::logger::DebugBytes(&self.0).fmt(f)
}
}
impl fmt::Display for ChannelInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "features: {}, node_one: {}, one_to_two: {:?}, node_two: {}, two_to_one: {:?}",
- log_bytes!(self.features.encode()), log_bytes!(self.node_one.as_slice()), self.one_to_two, log_bytes!(self.node_two.as_slice()), self.two_to_one)?;
+ log_bytes!(self.features.encode()), &self.node_one, self.one_to_two, &self.node_two, self.two_to_one)?;
Ok(())
}
}
}
writeln!(f, "[Nodes]")?;
for (&node_id, val) in self.nodes.read().unwrap().unordered_iter() {
- writeln!(f, " {}: {}", log_bytes!(node_id.as_slice()), val)?;
+ writeln!(f, " {}: {}", &node_id, val)?;
}
Ok(())
}
// This serialized info has an address field but no announcement_message, therefore the addresses returned by our function will still be empty
assert!(ann_info_with_addresses.addresses().is_empty());
}
+
+ #[test]
+ fn test_node_id_display() {
+ let node_id = NodeId([42; 33]);
+ assert_eq!(format!("{}", &node_id), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ }
}
#[cfg(ldk_bench)]
/// [`PaymentParameters::expiry_time`].
pub fn from_bolt12_invoice(invoice: &Bolt12Invoice) -> Self {
Self::blinded(invoice.payment_paths().to_vec())
- .with_bolt12_features(invoice.features().clone()).unwrap()
+ .with_bolt12_features(invoice.invoice_features().clone()).unwrap()
.with_expiry_time(invoice.created_at().as_secs().saturating_add(invoice.relative_expiry().as_secs()))
}
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::WPubkeyHash;
-use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
-use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing};
+use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
use bitcoin::secp256k1::ecdh::SharedSecret;
-use bitcoin::secp256k1::ecdsa::RecoverableSignature;
+use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
+use bitcoin::secp256k1::schnorr;
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
use crate::util::transaction_utils;
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
use crate::ln::script::ShutdownScript;
+use crate::offers::invoice::UnsignedBolt12Invoice;
+use crate::offers::invoice_request::UnsignedInvoiceRequest;
use crate::prelude::*;
use core::convert::TryInto;
use crate::util::chacha20::ChaCha20;
use crate::util::invoice::construct_invoice_preimage;
+pub(crate) mod type_resolver;
+
/// Used as initial key material, to be expanded into multiple secret keys (but not to be used
/// directly). This is used within LDK to encrypt/decrypt inbound payment data.
///
/// Errors if the [`Recipient`] variant is not supported by the implementation.
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()>;
+ /// Signs the [`TaggedHash`] of a BOLT 12 invoice request.
+ ///
+ /// May be called by a function passed to [`UnsignedInvoiceRequest::sign`] where
+ /// `invoice_request` is the callee.
+ ///
+ /// Implementors may check that the `invoice_request` is expected rather than blindly signing
+ /// the tagged hash. An `Ok` result should sign `invoice_request.tagged_hash().as_digest()` with
+ /// the node's signing key or an ephemeral key to preserve privacy, whichever is associated with
+ /// [`UnsignedInvoiceRequest::payer_id`].
+ ///
+ /// [`TaggedHash`]: crate::offers::merkle::TaggedHash
+ fn sign_bolt12_invoice_request(
+ &self, invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()>;
+
+ /// Signs the [`TaggedHash`] of a BOLT 12 invoice.
+ ///
+ /// May be called by a function passed to [`UnsignedBolt12Invoice::sign`] where `invoice` is the
+ /// callee.
+ ///
+ /// Implementors may check that the `invoice` is expected rather than blindly signing the tagged
+ /// hash. An `Ok` result should sign `invoice.tagged_hash().as_digest()` with the node's signing
+ /// key or an ephemeral key to preserve privacy, whichever is associated with
+ /// [`UnsignedBolt12Invoice::signing_pubkey`].
+ ///
+ /// [`TaggedHash`]: crate::offers::merkle::TaggedHash
+ fn sign_bolt12_invoice(
+ &self, invoice: &UnsignedBolt12Invoice
+ ) -> Result<schnorr::Signature, ()>;
+
/// Sign a gossip message.
///
/// Note that if this fails, LDK may panic and the message will not be broadcast to the network
Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
}
+ fn sign_bolt12_invoice_request(
+ &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 aux_rand = self.get_secure_random_bytes();
+ Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
+ }
+
+ fn sign_bolt12_invoice(
+ &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 aux_rand = self.get_secure_random_bytes();
+ Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
+ }
+
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
}
+ fn sign_bolt12_invoice_request(
+ &self, invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ self.inner.sign_bolt12_invoice_request(invoice_request)
+ }
+
+ fn sign_bolt12_invoice(
+ &self, invoice: &UnsignedBolt12Invoice
+ ) -> Result<schnorr::Signature, ()> {
+ self.inner.sign_bolt12_invoice(invoice)
+ }
+
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
self.inner.sign_gossip_message(msg)
}
--- /dev/null
+use crate::sign::{ChannelSigner, EcdsaChannelSigner};
+
+pub(crate) enum ChannelSignerType<ECS: EcdsaChannelSigner> {
+ // in practice, this will only ever be an EcdsaChannelSigner (specifically, Writeable)
+ Ecdsa(ECS)
+}
+
+impl<ECS: EcdsaChannelSigner> ChannelSignerType<ECS>{
+ pub(crate) fn as_ref(&self) -> &dyn ChannelSigner {
+ match self {
+ ChannelSignerType::Ecdsa(ecs) => ecs
+ }
+ }
+
+ pub(crate) fn as_mut(&mut self) -> &mut dyn ChannelSigner {
+ match self {
+ ChannelSignerType::Ecdsa(ecs) => ecs
+ }
+ }
+
+ pub(crate) fn as_ecdsa(&self) -> Option<&ECS> {
+ match self {
+ ChannelSignerType::Ecdsa(ecs) => Some(ecs)
+ }
+ }
+
+ pub(crate) fn as_mut_ecdsa(&mut self) -> Option<&mut ECS> {
+ match self {
+ ChannelSignerType::Ecdsa(ecs) => Some(ecs)
+ }
+ }
+}
use crate::ln::{msgs, wire};
use crate::ln::msgs::LightningError;
use crate::ln::script::ShutdownScript;
+use crate::offers::invoice::UnsignedBolt12Invoice;
+use crate::offers::invoice_request::UnsignedInvoiceRequest;
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, Router, ScorerAccountingForInFlightHtlcs};
use bitcoin::hash_types::{BlockHash, Txid};
use bitcoin::util::sighash::SighashCache;
-use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, ecdsa::Signature, Scalar};
+use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
use bitcoin::secp256k1::ecdh::SharedSecret;
-use bitcoin::secp256k1::ecdsa::RecoverableSignature;
+use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
+use bitcoin::secp256k1::schnorr;
#[cfg(any(test, feature = "_test_utils"))]
use regex;
unreachable!()
}
+ fn sign_bolt12_invoice_request(
+ &self, _invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
+ fn sign_bolt12_invoice(
+ &self, _invoice: &UnsignedBolt12Invoice,
+ ) -> Result<schnorr::Signature, ()> {
+ unreachable!()
+ }
+
fn sign_gossip_message(&self, _msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
unreachable!()
}
self.backing.sign_invoice(hrp_bytes, invoice_data, recipient)
}
+ fn sign_bolt12_invoice_request(
+ &self, invoice_request: &UnsignedInvoiceRequest
+ ) -> Result<schnorr::Signature, ()> {
+ self.backing.sign_bolt12_invoice_request(invoice_request)
+ }
+
+ fn sign_bolt12_invoice(
+ &self, invoice: &UnsignedBolt12Invoice,
+ ) -> Result<schnorr::Signature, ()> {
+ self.backing.sign_bolt12_invoice(invoice)
+ }
+
fn sign_gossip_message(&self, msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
self.backing.sign_gossip_message(msg)
}