ShutdownScript::new_p2wpkh(&pubkey_hash)
}
- fn get_channel_signer(&self, _inbound: bool, channel_value_satoshis: u64) -> EnforcingSigner {
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] {
+ let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed) as u8;
+ [id; 32]
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
let secp_ctx = Secp256k1::signing_only();
- let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed);
+ let id = channel_keys_id[0];
let keys = InMemorySigner::new(
&secp_ctx,
self.get_node_secret(Recipient::Node).unwrap(),
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_id]).unwrap(),
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, self.node_id]).unwrap(),
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_id]).unwrap(),
- [id as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_id],
+ [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_id],
channel_value_satoshis,
- [0; 32],
+ channel_keys_id,
);
let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
EnforcingSigner::new_with_revoked(keys, revoked_commitment, false)
match api_err {
APIError::APIMisuseError { .. } => panic!("We can't misuse the API"),
APIError::FeeRateTooHigh { .. } => panic!("We can't send too much fee?"),
- APIError::RouteError { .. } => panic!("Our routes should work"),
+ APIError::InvalidRoute { .. } => panic!("Our routes should work"),
APIError::ChannelUnavailable { err } => {
// Test the error against a list of errors we can hit, and reject
// all others. If you hit this panic, the list of acceptable errors
let mut events = nodes[$node].get_and_clear_pending_events();
// Sort events so that PendingHTLCsForwardable get processed last. This avoids a
// case where we first process a PendingHTLCsForwardable, then claim/fail on a
- // PaymentReceived, claiming/failing two HTLCs, but leaving a just-generated
- // PaymentReceived event for the second HTLC in our pending_events (and breaking
+ // PaymentClaimable, claiming/failing two HTLCs, but leaving a just-generated
+ // PaymentClaimable event for the second HTLC in our pending_events (and breaking
// our claim_set deduplication).
events.sort_by(|a, b| {
- if let events::Event::PaymentReceived { .. } = a {
+ if let events::Event::PaymentClaimable { .. } = a {
if let events::Event::PendingHTLCsForwardable { .. } = b {
Ordering::Less
} else { Ordering::Equal }
} else if let events::Event::PendingHTLCsForwardable { .. } = a {
- if let events::Event::PaymentReceived { .. } = b {
+ if let events::Event::PaymentClaimable { .. } = b {
Ordering::Greater
} else { Ordering::Equal }
} else { Ordering::Equal }
let had_events = !events.is_empty();
for event in events.drain(..) {
match event {
- events::Event::PaymentReceived { payment_hash, .. } => {
+ events::Event::PaymentClaimable { payment_hash, .. } => {
if claim_set.insert(payment_hash.0) {
if $fail {
nodes[$node].fail_htlc_backwards(&payment_hash);
node_secret: SecretKey,
inbound_payment_key: KeyMaterial,
counter: AtomicU64,
+ signer_state: RefCell<HashMap<u8, (bool, Arc<Mutex<EnforcementState>>)>>
}
impl KeysInterface for KeyProvider {
type Signer = EnforcingSigner;
ShutdownScript::new_p2wpkh(&pubkey_hash)
}
- fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> EnforcingSigner {
+ fn generate_channel_keys_id(&self, inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] {
let ctr = self.counter.fetch_add(1, Ordering::Relaxed) as u8;
+ self.signer_state.borrow_mut().insert(ctr, (inbound, Arc::new(Mutex::new(EnforcementState::new()))));
+ [ctr; 32]
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
let secp_ctx = Secp256k1::signing_only();
- EnforcingSigner::new(if inbound {
+ let ctr = channel_keys_id[0];
+ let (inbound, state) = self.signer_state.borrow().get(&ctr).unwrap().clone();
+ EnforcingSigner::new_with_revoked(if inbound {
InMemorySigner::new(
&secp_ctx,
self.node_secret.clone(),
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, ctr]).unwrap(),
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, ctr],
channel_value_satoshis,
- [0; 32],
+ channel_keys_id,
)
} else {
InMemorySigner::new(
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, ctr]).unwrap(),
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, ctr],
channel_value_satoshis,
- [0; 32],
+ channel_keys_id,
)
- })
+ }, state, false)
}
fn get_secure_random_bytes(&self) -> [u8; 32] {
let monitor = Arc::new(chainmonitor::ChainMonitor::new(None, broadcast.clone(), Arc::clone(&logger), fee_est.clone(),
Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) })));
- let keys_manager = Arc::new(KeyProvider { node_secret: our_network_key.clone(), inbound_payment_key: KeyMaterial(inbound_payment_key.try_into().unwrap()), counter: AtomicU64::new(0) });
+ let keys_manager = Arc::new(KeyProvider {
+ node_secret: our_network_key.clone(),
+ inbound_payment_key: KeyMaterial(inbound_payment_key.try_into().unwrap()),
+ counter: AtomicU64::new(0),
+ signer_state: RefCell::new(HashMap::new())
+ });
let mut config = UserConfig::default();
config.channel_config.forwarding_fee_proportional_millionths = slice_to_be32(get_slice!(4));
config.channel_handshake_config.announced_channel = get_slice!(1)[0] != 0;
Event::FundingGenerationReady { temporary_channel_id, counterparty_node_id, channel_value_satoshis, output_script, .. } => {
pending_funding_generation.push((temporary_channel_id, counterparty_node_id, channel_value_satoshis, output_script));
},
- Event::PaymentReceived { payment_hash, .. } => {
+ Event::PaymentClaimable { payment_hash, .. } => {
//TODO: enhance by fetching random amounts from fuzz input?
payments_received.push(payment_hash);
},
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!() }
- fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> EnforcingSigner {
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] { unreachable!() }
+
+ fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::Signer {
unreachable!()
}
nodes[fwd_idx].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
commitment_signed_dance!(nodes[fwd_idx], nodes[0], &payment_event.commitment_msg, false, true);
- // Note that we have to "forward pending HTLCs" twice before we see the PaymentReceived as
+ // Note that we have to "forward pending HTLCs" twice before we see the PaymentClaimable as
// this "emulates" the payment taking two hops, providing some privacy to make phantom node
// payments "look real" by taking more time.
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
nodes[fwd_idx].node.process_pending_htlc_forwards();
let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
- expect_payment_received!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, route.paths[0].last().unwrap().pubkey);
+ expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, route.paths[0].last().unwrap().pubkey);
do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[fwd_idx])[..]), false, payment_preimage);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
use crate::util::byte_utils;
use crate::util::events::Event;
#[cfg(anchors)]
-use crate::util::events::{AnchorDescriptor, BumpTransactionEvent};
+use crate::util::events::{AnchorDescriptor, HTLCDescriptor, BumpTransactionEvent};
use crate::prelude::*;
use core::{cmp, mem};
impl Writeable for CounterpartyCommitmentParameters {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.write_all(&byte_utils::be64_to_array(0))?;
+ w.write_all(&(0 as u64).to_be_bytes())?;
write_tlv_fields!(w, {
(0, self.counterparty_delayed_payment_base_key, required),
(2, self.counterparty_htlc_base_key, required),
/// was not present in the confirmed commitment transaction), HTLC-Success, or HTLC-Timeout
/// transaction.
resolving_txid: Option<Txid>, // Added as optional, but always filled in, in 0.0.110
+ resolving_tx: Option<Transaction>,
/// Only set if the HTLC claim was ours using a payment preimage
payment_preimage: Option<PaymentPreimage>,
}
(0, mapped_commitment_tx_output_idx, required),
(1, self.resolving_txid, option),
(2, self.payment_preimage, option),
+ (3, self.resolving_tx, option),
});
Ok(())
}
let mut mapped_commitment_tx_output_idx = 0;
let mut resolving_txid = None;
let mut payment_preimage = None;
+ let mut resolving_tx = None;
read_tlv_fields!(reader, {
(0, mapped_commitment_tx_output_idx, required),
(1, resolving_txid, option),
(2, payment_preimage, option),
+ (3, resolving_tx, option),
});
Ok(Self {
commitment_tx_output_idx: if mapped_commitment_tx_output_idx == u32::max_value() { None } else { Some(mapped_commitment_tx_output_idx) },
resolving_txid,
payment_preimage,
+ resolving_tx,
})
}
}
self.channel_keys_id.write(writer)?;
self.holder_revocation_basepoint.write(writer)?;
writer.write_all(&self.funding_info.0.txid[..])?;
- writer.write_all(&byte_utils::be16_to_array(self.funding_info.0.index))?;
+ writer.write_all(&self.funding_info.0.index.to_be_bytes())?;
self.funding_info.1.write(writer)?;
self.current_counterparty_commitment_txid.write(writer)?;
self.prev_counterparty_commitment_txid.write(writer)?;
},
}
- writer.write_all(&byte_utils::be16_to_array(self.on_holder_tx_csv))?;
+ writer.write_all(&self.on_holder_tx_csv.to_be_bytes())?;
self.commitment_secrets.write(writer)?;
macro_rules! serialize_htlc_in_commitment {
($htlc_output: expr) => {
writer.write_all(&[$htlc_output.offered as u8; 1])?;
- writer.write_all(&byte_utils::be64_to_array($htlc_output.amount_msat))?;
- writer.write_all(&byte_utils::be32_to_array($htlc_output.cltv_expiry))?;
+ writer.write_all(&$htlc_output.amount_msat.to_be_bytes())?;
+ writer.write_all(&$htlc_output.cltv_expiry.to_be_bytes())?;
writer.write_all(&$htlc_output.payment_hash.0[..])?;
$htlc_output.transaction_output_index.write(writer)?;
}
}
- writer.write_all(&byte_utils::be64_to_array(self.counterparty_claimable_outpoints.len() as u64))?;
+ writer.write_all(&(self.counterparty_claimable_outpoints.len() as u64).to_be_bytes())?;
for (ref txid, ref htlc_infos) in self.counterparty_claimable_outpoints.iter() {
writer.write_all(&txid[..])?;
- writer.write_all(&byte_utils::be64_to_array(htlc_infos.len() as u64))?;
+ writer.write_all(&(htlc_infos.len() as u64).to_be_bytes())?;
for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
debug_assert!(htlc_source.is_none() || Some(**txid) == self.current_counterparty_commitment_txid
|| Some(**txid) == self.prev_counterparty_commitment_txid,
}
}
- writer.write_all(&byte_utils::be64_to_array(self.counterparty_commitment_txn_on_chain.len() as u64))?;
+ writer.write_all(&(self.counterparty_commitment_txn_on_chain.len() as u64).to_be_bytes())?;
for (ref txid, commitment_number) in self.counterparty_commitment_txn_on_chain.iter() {
writer.write_all(&txid[..])?;
writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
}
- writer.write_all(&byte_utils::be64_to_array(self.counterparty_hash_commitment_number.len() as u64))?;
+ writer.write_all(&(self.counterparty_hash_commitment_number.len() as u64).to_be_bytes())?;
for (ref payment_hash, commitment_number) in self.counterparty_hash_commitment_number.iter() {
writer.write_all(&payment_hash.0[..])?;
writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
writer.write_all(&byte_utils::be48_to_array(self.current_counterparty_commitment_number))?;
writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
- writer.write_all(&byte_utils::be64_to_array(self.payment_preimages.len() as u64))?;
+ writer.write_all(&(self.payment_preimages.len() as u64).to_be_bytes())?;
for payment_preimage in self.payment_preimages.values() {
writer.write_all(&payment_preimage.0[..])?;
}
}
}
- writer.write_all(&byte_utils::be64_to_array(self.pending_events.len() as u64))?;
+ writer.write_all(&(self.pending_events.len() as u64).to_be_bytes())?;
for event in self.pending_events.iter() {
event.write(writer)?;
}
self.best_block.block_hash().write(writer)?;
- writer.write_all(&byte_utils::be32_to_array(self.best_block.height()))?;
+ writer.write_all(&self.best_block.height().to_be_bytes())?;
- writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?;
+ writer.write_all(&(self.onchain_events_awaiting_threshold_conf.len() as u64).to_be_bytes())?;
for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
entry.write(writer)?;
}
if let Some(v) = htlc.transaction_output_index { v } else { return None; };
let mut htlc_spend_txid_opt = None;
+ let mut htlc_spend_tx_opt = None;
let mut holder_timeout_spend_pending = None;
let mut htlc_spend_pending = None;
let mut holder_delayed_output_pending = None;
OnchainEvent::HTLCUpdate { commitment_tx_output_idx, htlc_value_satoshis, .. }
if commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) => {
debug_assert!(htlc_spend_txid_opt.is_none());
- htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
+ htlc_spend_txid_opt = Some(&event.txid);
+ debug_assert!(htlc_spend_tx_opt.is_none());
+ htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(holder_timeout_spend_pending.is_none());
debug_assert_eq!(htlc_value_satoshis.unwrap(), htlc.amount_msat / 1000);
holder_timeout_spend_pending = Some(event.confirmation_threshold());
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. }
if commitment_tx_output_idx == htlc_commitment_tx_output_idx => {
debug_assert!(htlc_spend_txid_opt.is_none());
- htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
+ htlc_spend_txid_opt = Some(&event.txid);
+ debug_assert!(htlc_spend_tx_opt.is_none());
+ htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(htlc_spend_pending.is_none());
htlc_spend_pending = Some((event.confirmation_threshold(), preimage.is_some()));
},
let htlc_resolved = self.htlcs_resolved_on_chain.iter()
.find(|v| if v.commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) {
debug_assert!(htlc_spend_txid_opt.is_none());
- htlc_spend_txid_opt = v.resolving_txid;
+ htlc_spend_txid_opt = v.resolving_txid.as_ref();
+ debug_assert!(htlc_spend_tx_opt.is_none());
+ htlc_spend_tx_opt = v.resolving_tx.as_ref();
true
} else { false });
debug_assert!(holder_timeout_spend_pending.is_some() as u8 + htlc_spend_pending.is_some() as u8 + htlc_resolved.is_some() as u8 <= 1);
+ let htlc_commitment_outpoint = BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx);
let htlc_output_to_spend =
if let Some(txid) = htlc_spend_txid_opt {
- debug_assert!(
- self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_none(),
- "This code needs updating for anchors");
- BitcoinOutPoint::new(txid, 0)
+ // Because HTLC transactions either only have 1 input and 1 output (pre-anchors) or
+ // are signed with SIGHASH_SINGLE|ANYONECANPAY under BIP-0143 (post-anchors), we can
+ // locate the correct output by ensuring its adjacent input spends the HTLC output
+ // in the commitment.
+ if let Some(ref tx) = htlc_spend_tx_opt {
+ let htlc_input_idx_opt = tx.input.iter().enumerate()
+ .find(|(_, input)| input.previous_output == htlc_commitment_outpoint)
+ .map(|(idx, _)| idx as u32);
+ debug_assert!(htlc_input_idx_opt.is_some());
+ BitcoinOutPoint::new(*txid, htlc_input_idx_opt.unwrap_or(0))
+ } else {
+ debug_assert!(!self.onchain_tx_handler.opt_anchors());
+ BitcoinOutPoint::new(*txid, 0)
+ }
} else {
- BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx)
+ htlc_commitment_outpoint
};
let htlc_output_spend_pending = self.onchain_tx_handler.is_output_spend_pending(&htlc_output_to_spend);
} = &event.event {
if event.transaction.as_ref().map(|tx| tx.input.iter().any(|inp| {
if let Some(htlc_spend_txid) = htlc_spend_txid_opt {
- Some(tx.txid()) == htlc_spend_txid_opt ||
- inp.previous_output.txid == htlc_spend_txid
+ tx.txid() == *htlc_spend_txid || inp.previous_output.txid == *htlc_spend_txid
} else {
Some(inp.previous_output.txid) == confirmed_txid &&
inp.previous_output.vout == htlc_commitment_tx_output_idx
res
}
+ /// Gets the set of outbound HTLCs which can be (or have been) resolved by this
+ /// `ChannelMonitor`. This is used to determine if an HTLC was removed from the channel prior
+ /// to the `ChannelManager` having been persisted.
+ ///
+ /// This is similar to [`Self::get_pending_outbound_htlcs`] except it includes HTLCs which were
+ /// resolved by this `ChannelMonitor`.
+ pub(crate) fn get_all_current_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
+ let mut res = HashMap::new();
+ // Just examine the available counterparty commitment transactions. See docs on
+ // `fail_unbroadcast_htlcs`, below, for justification.
+ let us = self.inner.lock().unwrap();
+ macro_rules! walk_counterparty_commitment {
+ ($txid: expr) => {
+ if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
+ for &(ref htlc, ref source_option) in latest_outpoints.iter() {
+ if let &Some(ref source) = source_option {
+ res.insert((**source).clone(), htlc.clone());
+ }
+ }
+ }
+ }
+ }
+ if let Some(ref txid) = us.current_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ if let Some(ref txid) = us.prev_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ res
+ }
+
/// Gets the set of outbound HTLCs which are pending resolution in this channel.
/// This is used to reconstruct pending outbound payments on restart in the ChannelManager.
pub(crate) fn get_pending_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
- let mut res = HashMap::new();
let us = self.inner.lock().unwrap();
+ // We're only concerned with the confirmation count of HTLC transactions, and don't
+ // actually care how many confirmations a commitment transaction may or may not have. Thus,
+ // we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
+ let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
+ us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
+ Some(event.txid)
+ } else { None }
+ })
+ });
+ if confirmed_txid.is_none() {
+ // If we have not seen a commitment transaction on-chain (ie the channel is not yet
+ // closed), just get the full set.
+ mem::drop(us);
+ return self.get_all_current_outbound_htlcs();
+ }
+
+ let mut res = HashMap::new();
macro_rules! walk_htlcs {
($holder_commitment: expr, $htlc_iter: expr) => {
for (htlc, source) in $htlc_iter {
}
}
- // We're only concerned with the confirmation count of HTLC transactions, and don't
- // actually care how many confirmations a commitment transaction may or may not have. Thus,
- // we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
- let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
- us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
- if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
- Some(event.txid)
+ let txid = confirmed_txid.unwrap();
+ if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
+ walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
+ if let &Some(ref source) = b {
+ Some((a, &**source))
} else { None }
- })
- });
- if let Some(txid) = confirmed_txid {
- if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
- walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
- if let &Some(ref source) = b {
- Some((a, &**source))
- } else { None }
- }));
- } else if txid == us.current_holder_commitment_tx.txid {
- walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ }));
+ } else if txid == us.current_holder_commitment_tx.txid {
+ walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ if let Some(source) = c { Some((a, source)) } else { None }
+ }));
+ } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
+ if txid == prev_commitment.txid {
+ walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
if let Some(source) = c { Some((a, source)) } else { None }
}));
- } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
- if txid == prev_commitment.txid {
- walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
- if let Some(source) = c { Some((a, source)) } else { None }
- }));
- }
- }
- } else {
- // If we have not seen a commitment transaction on-chain (ie the channel is not yet
- // closed), just examine the available counterparty commitment transactions. See docs
- // on `fail_unbroadcast_htlcs`, below, for justification.
- macro_rules! walk_counterparty_commitment {
- ($txid: expr) => {
- if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
- for &(ref htlc, ref source_option) in latest_outpoints.iter() {
- if let &Some(ref source) = source_option {
- res.insert((**source).clone(), htlc.clone());
- }
- }
- }
- }
- }
- if let Some(ref txid) = us.current_counterparty_commitment_txid {
- walk_counterparty_commitment!(txid);
- }
- if let Some(ref txid) = us.prev_counterparty_commitment_txid {
- walk_counterparty_commitment!(txid);
}
}
pending_htlcs,
}));
},
+ ClaimEvent::BumpHTLC {
+ target_feerate_sat_per_1000_weight, htlcs,
+ } => {
+ let mut htlc_descriptors = Vec::with_capacity(htlcs.len());
+ for htlc in htlcs {
+ htlc_descriptors.push(HTLCDescriptor {
+ channel_keys_id: self.channel_keys_id,
+ channel_value_satoshis: self.channel_value_satoshis,
+ channel_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
+ commitment_txid: htlc.commitment_txid,
+ per_commitment_number: htlc.per_commitment_number,
+ htlc: htlc.htlc,
+ preimage: htlc.preimage,
+ counterparty_sig: htlc.counterparty_sig,
+ });
+ }
+ ret.push(Event::BumpTransaction(BumpTransactionEvent::HTLCResolution {
+ target_feerate_sat_per_1000_weight,
+ htlc_descriptors,
+ }));
+ }
}
}
ret
let secret = self.get_secret(commitment_number).unwrap();
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
- let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint));
- let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_commitment_params.counterparty_delayed_payment_base_key));
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint);
+ let delayed_key = chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
} else { return (claimable_outpoints, to_counterparty_output_info); };
if let Some(transaction) = tx {
- let revokeable_p2wsh_opt =
- if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
- &self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
- {
- if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
- &per_commitment_point,
- &self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
- {
- Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
- self.counterparty_commitment_params.on_counterparty_tx_csv,
- &delayed_key).to_v0_p2wsh())
- } else {
- debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
- None
- }
- } else {
- debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
- None
- };
- if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
- for (idx, outp) in transaction.output.iter().enumerate() {
- if outp.script_pubkey == revokeable_p2wsh {
- to_counterparty_output_info =
- Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
- }
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(
+ &self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint);
+ let delayed_key = chan_utils::derive_public_key(&self.secp_ctx,
+ &per_commitment_point,
+ &self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
+ let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
+ self.counterparty_commitment_params.on_counterparty_tx_csv,
+ &delayed_key).to_v0_p2wsh();
+ for (idx, outp) in transaction.output.iter().enumerate() {
+ if outp.script_pubkey == revokeable_p2wsh {
+ to_counterparty_output_info =
+ Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
}
}
}
}
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
- fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
- let htlc_txid = tx.txid();
- if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
- return (Vec::new(), None)
- }
-
- macro_rules! ignore_error {
- ( $thing : expr ) => {
- match $thing {
- Ok(a) => a,
- Err(_) => return (Vec::new(), None)
- }
- };
- }
-
+ fn check_spend_counterparty_htlc<L: Deref>(
+ &mut self, tx: &Transaction, commitment_number: u64, commitment_txid: &Txid, height: u32, logger: &L
+ ) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
- let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
+ let per_commitment_key = match SecretKey::from_slice(&secret) {
+ Ok(key) => key,
+ Err(_) => return (Vec::new(), None)
+ };
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
- log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0);
- let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_commitment_params.on_counterparty_tx_csv);
- let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
- let claimable_outpoints = vec!(justice_package);
- let outputs = vec![(0, tx.output[0].clone())];
- (claimable_outpoints, Some((htlc_txid, outputs)))
+ let htlc_txid = tx.txid();
+ let mut claimable_outpoints = vec![];
+ let mut outputs_to_watch = None;
+ // Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
+ // with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
+ // counterparty can now aggregate multiple HTLCs into a single transaction thanks to
+ // `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
+ // confirmed revoked HTLC transaction (for more details, see
+ // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
+ //
+ // We make sure we're not vulnerable to this case by checking all inputs of the transaction,
+ // and claim those which spend the commitment transaction, have a witness of 5 elements, and
+ // have a corresponding output at the same index within the transaction.
+ for (idx, input) in tx.input.iter().enumerate() {
+ if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {
+ log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, idx);
+ let revk_outp = RevokedOutput::build(
+ per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
+ self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
+ tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv
+ );
+ let justice_package = PackageTemplate::build_package(
+ htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),
+ height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height
+ );
+ claimable_outpoints.push(justice_package);
+ if outputs_to_watch.is_none() {
+ outputs_to_watch = Some((htlc_txid, vec![]));
+ }
+ outputs_to_watch.as_mut().unwrap().1.push((idx as u32, tx.output[idx].clone()));
+ }
+ }
+ (claimable_outpoints, outputs_to_watch)
}
// Returns (1) `PackageTemplate`s that can be given to the OnchainTxHandler, so that the handler can
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- let htlc_output = if htlc.offered {
- HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
+ let (htlc_output, aggregable) = if htlc.offered {
+ let htlc_output = HolderHTLCOutput::build_offered(
+ htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.opt_anchors()
+ );
+ (htlc_output, false)
+ } else {
+ let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
+ preimage.clone()
} else {
- let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
- preimage.clone()
- } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- };
- HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
};
- let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), htlc.cltv_expiry, false, conf_height);
+ let htlc_output = HolderHTLCOutput::build_accepted(
+ payment_preimage, htlc.amount_msat, self.onchain_tx_handler.opt_anchors()
+ );
+ (htlc_output, self.onchain_tx_handler.opt_anchors())
+ };
+ let htlc_package = PackageTemplate::build_package(
+ holder_tx.txid, transaction_output_index,
+ PackageSolvingData::HolderHTLCOutput(htlc_output),
+ htlc.cltv_expiry, aggregable, conf_height
+ );
claim_requests.push(htlc_package);
}
}
if tx.input.len() == 1 {
// Assuming our keys were not leaked (in which case we're screwed no matter what),
- // commitment transactions and HTLC transactions will all only ever have one input,
- // which is an easy way to filter out any potential non-matching txn for lazy
- // filters.
+ // commitment transactions and HTLC transactions will all only ever have one input
+ // (except for HTLC transactions for channels with anchor outputs), which is an easy
+ // way to filter out any potential non-matching txn for lazy filters.
let prevout = &tx.input[0].previous_output;
if prevout.txid == self.funding_info.0.txid && prevout.vout == self.funding_info.0.index as u32 {
let mut balance_spendable_csv = None;
commitment_tx_to_counterparty_output,
},
});
- } else {
- if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&prevout.txid) {
- let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(&tx, commitment_number, height, &logger);
+ }
+ }
+ if tx.input.len() >= 1 {
+ // While all commitment transactions have one input, HTLC transactions may have more
+ // if the HTLC was present in an anchor channel. HTLCs can also be resolved in a few
+ // other ways which can have more than one output.
+ for tx_input in &tx.input {
+ let commitment_txid = tx_input.previous_output.txid;
+ if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&commitment_txid) {
+ let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(
+ &tx, commitment_number, &commitment_txid, height, &logger
+ );
claimable_outpoints.append(&mut new_outpoints);
if let Some(new_outputs) = new_outputs_option {
watch_outputs.push(new_outputs);
}
+ // Since there may be multiple HTLCs for this channel (all spending the
+ // same commitment tx) being claimed by the counterparty within the same
+ // transaction, and `check_spend_counterparty_htlc` already checks all the
+ // ones relevant to this channel, we can safely break from our loop.
+ break;
}
}
- }
- // While all commitment/HTLC-Success/HTLC-Timeout transactions have one input, HTLCs
- // can also be resolved in a few other ways which can have more than one output. Thus,
- // we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check.
- self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
+ self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
- self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
+ self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
+ }
}
if height > self.best_block.height() {
htlc_value_satoshis,
}));
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC {
- commitment_tx_output_idx, resolving_txid: Some(entry.txid),
+ commitment_tx_output_idx,
+ resolving_txid: Some(entry.txid),
+ resolving_tx: entry.transaction,
payment_preimage: None,
});
},
},
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC {
- commitment_tx_output_idx: Some(commitment_tx_output_idx), resolving_txid: Some(entry.txid),
+ commitment_tx_output_idx: Some(commitment_tx_output_idx),
+ resolving_txid: Some(entry.txid),
+ resolving_tx: entry.transaction,
payment_preimage: preimage,
});
},
return Err(DecodeError::InvalidValue);
}
}
- let onchain_tx_handler: OnchainTxHandler<K::Signer> = ReadableArgs::read(reader, keys_manager)?;
+ let onchain_tx_handler: OnchainTxHandler<K::Signer> = ReadableArgs::read(
+ reader, (keys_manager, channel_value_satoshis, channel_keys_id)
+ )?;
let lockdown_from_offchain = Readable::read(reader)?;
let holder_tx_signed = Readable::read(reader)?;
// You may not use this file except in accordance with one or both of these
// licenses.
-//! keysinterface provides keys into rust-lightning and defines some useful enums which describe
-//! spendable on-chain outputs which the user owns and is responsible for using just as any other
-//! on-chain output which is theirs.
+//! Provides keys to LDK and defines some useful objects describing spendable on-chain outputs.
+//!
+//! The provided output descriptors follow a custom LDK data format and are currently not fully
+//! compatible with Bitcoin Core output descriptors.
use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, EcdsaSighashType};
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
-use crate::util::{byte_utils, transaction_utils};
+use crate::util::transaction_utils;
use crate::util::crypto::{hkdf_extract_expand_twice, sign};
use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
-
+#[cfg(anchors)]
+use crate::util::events::HTLCDescriptor;
use crate::chain::transaction::OutPoint;
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::ln::{chan_utils, PaymentPreimage};
use crate::ln::script::ShutdownScript;
use crate::prelude::*;
+use core::convert::TryInto;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::io::{self, Error};
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
/// 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.
-/// (C-not exported) as we just use [u8; 32] directly
+///
+/// (C-not exported) as we just use `[u8; 32]` directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
pub struct KeyMaterial(pub [u8; 32]);
-/// Information about a spendable output to a P2WSH script. See
-/// SpendableOutputDescriptor::DelayedPaymentOutput for more details on how to spend this.
+/// Information about a spendable output to a P2WSH script.
+///
+/// See [`SpendableOutputDescriptor::DelayedPaymentOutput`] for more details on how to spend this.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DelayedPaymentOutputDescriptor {
- /// The outpoint which is spendable
+ /// The outpoint which is spendable.
pub outpoint: OutPoint,
- /// Per commitment point to derive delayed_payment_key by key holder
+ /// Per commitment point to derive the delayed payment key by key holder.
pub per_commitment_point: PublicKey,
- /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
+ /// The `nSequence` value which must be set in the spending input to satisfy the `OP_CSV` in
/// the witness_script.
pub to_self_delay: u16,
- /// The output which is referenced by the given outpoint
+ /// The output which is referenced by the given outpoint.
pub output: TxOut,
/// The revocation point specific to the commitment transaction which was broadcast. Used to
/// derive the witnessScript for this output.
pub revocation_pubkey: PublicKey,
- /// Arbitrary identification information returned by a call to
- /// `Sign::channel_keys_id()`. This may be useful in re-deriving keys used in
- /// the channel to spend the output.
+ /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`].
+ /// This may be useful in re-deriving keys used in the channel to spend the output.
pub channel_keys_id: [u8; 32],
/// The value of the channel which this output originated from, possibly indirectly.
pub channel_value_satoshis: u64,
(12, channel_value_satoshis, required),
});
-/// Information about a spendable output to our "payment key". See
-/// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this.
+/// Information about a spendable output to our "payment key".
+///
+/// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StaticPaymentOutputDescriptor {
- /// The outpoint which is spendable
+ /// The outpoint which is spendable.
pub outpoint: OutPoint,
- /// The output which is referenced by the given outpoint
+ /// The output which is referenced by the given outpoint.
pub output: TxOut,
- /// Arbitrary identification information returned by a call to
- /// `Sign::channel_keys_id()`. This may be useful in re-deriving keys used in
- /// the channel to spend the output.
+ /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`].
+ /// This may be useful in re-deriving keys used in the channel to spend the output.
pub channel_keys_id: [u8; 32],
/// The value of the channel which this transactions spends.
pub channel_value_satoshis: u64,
(6, channel_value_satoshis, required),
});
-/// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
-/// claim at any point in the future) an event is generated which you must track and be able to
-/// spend on-chain. The information needed to do this is provided in this enum, including the
-/// outpoint describing which txid and output index is available, the full output which exists at
-/// that txid/index, and any keys or other information required to sign.
+/// Describes the necessary information to spend a spendable output.
+///
+/// When on-chain outputs are created by LDK (which our counterparty is not able to claim at any
+/// point in the future) a [`SpendableOutputs`] event is generated which you must track and be able
+/// to spend on-chain. The information needed to do this is provided in this enum, including the
+/// outpoint describing which `txid` and output `index` is available, the full output which exists
+/// at that `txid`/`index`, and any keys or other information required to sign.
+///
+/// [`SpendableOutputs`]: crate::util::events::Event::SpendableOutputs
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SpendableOutputDescriptor {
- /// An output to a script which was provided via KeysInterface directly, either from
- /// `get_destination_script()` or `get_shutdown_scriptpubkey()`, thus you should already know
- /// how to spend it. No secret keys are provided as rust-lightning was never given any key.
+ /// An output to a script which was provided via [`KeysInterface`] directly, either from
+ /// [`get_destination_script`] or [`get_shutdown_scriptpubkey`], thus you should already
+ /// know how to spend it. No secret keys are provided as LDK was never given any key.
/// These may include outputs from a transaction punishing our counterparty or claiming an HTLC
/// on-chain using the payment preimage or after it has timed out.
+ ///
+ /// [`get_shutdown_scriptpubkey`]: KeysInterface::get_shutdown_scriptpubkey
+ /// [`get_destination_script`]: KeysInterface::get_shutdown_scriptpubkey
StaticOutput {
- /// The outpoint which is spendable
+ /// The outpoint which is spendable.
outpoint: OutPoint,
/// The output which is referenced by the given outpoint.
output: TxOut,
},
- /// An output to a P2WSH script which can be spent with a single signature after a CSV delay.
+ /// An output to a P2WSH script which can be spent with a single signature after an `OP_CSV`
+ /// delay.
///
/// The witness in the spending input should be:
+ /// ```bitcoin
/// <BIP 143 signature> <empty vector> (MINIMALIF standard rule) <provided witnessScript>
+ /// ```
///
- /// Note that the nSequence field in the spending input must be set to to_self_delay
- /// (which means the transaction is not broadcastable until at least to_self_delay
- /// blocks after the outpoint confirms).
+ /// Note that the `nSequence` field in the spending input must be set to
+ /// [`DelayedPaymentOutputDescriptor::to_self_delay`] (which means the transaction is not
+ /// broadcastable until at least [`DelayedPaymentOutputDescriptor::to_self_delay`] blocks after
+ /// the outpoint confirms, see [BIP
+ /// 68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki)). Also note that LDK
+ /// won't generate a [`SpendableOutputDescriptor`] until the corresponding block height
+ /// is reached.
///
/// These are generally the result of a "revocable" output to us, spendable only by us unless
/// it is an output from an old state which we broadcast (which should never happen).
///
- /// To derive the delayed_payment key which is used to sign for this input, you must pass the
- /// holder delayed_payment_base_key (ie the private key which corresponds to the pubkey in
- /// Sign::pubkeys().delayed_payment_basepoint) and the provided per_commitment_point to
- /// chan_utils::derive_private_key. The public key can be generated without the secret key
- /// using chan_utils::derive_public_key and only the delayed_payment_basepoint which appears in
- /// Sign::pubkeys().
- ///
- /// To derive the revocation_pubkey provided here (which is used in the witness
- /// script generation), you must pass the counterparty revocation_basepoint (which appears in the
- /// call to Sign::ready_channel) and the provided per_commitment point
- /// to chan_utils::derive_public_revocation_key.
- ///
- /// The witness script which is hashed and included in the output script_pubkey may be
- /// regenerated by passing the revocation_pubkey (derived as above), our delayed_payment pubkey
- /// (derived as above), and the to_self_delay contained here to
- /// chan_utils::get_revokeable_redeemscript.
+ /// To derive the delayed payment key which is used to sign this input, you must pass the
+ /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`BaseSign::pubkeys`]) and the provided
+ /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be
+ /// generated without the secret key using [`chan_utils::derive_public_key`] and only the
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`BaseSign::pubkeys`].
+ ///
+ /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is
+ /// used in the witness script generation), you must pass the counterparty
+ /// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to
+ /// [`BaseSign::provide_channel_parameters`]) and the provided
+ /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to
+ /// [`chan_utils::derive_public_revocation_key`].
+ ///
+ /// The witness script which is hashed and included in the output `script_pubkey` may be
+ /// regenerated by passing the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] (derived
+ /// as explained above), our delayed payment pubkey (derived as explained above), and the
+ /// [`DelayedPaymentOutputDescriptor::to_self_delay`] contained here to
+ /// [`chan_utils::get_revokeable_redeemscript`].
DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
- /// An output to a P2WPKH, spendable exclusively by our payment key (ie the private key which
- /// corresponds to the public key in Sign::pubkeys().payment_point).
- /// The witness in the spending input, is, thus, simply:
+ /// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key
+ /// which corresponds to the `payment_point` in [`BaseSign::pubkeys`]). The witness
+ /// in the spending input is, thus, simply:
+ /// ```bitcoin
/// <BIP 143 signature> <payment key>
+ /// ```
///
/// These are generally the result of our counterparty having broadcast the current state,
/// allowing us to claim the non-HTLC-encumbered outputs immediately.
(2, StaticPaymentOutput),
);
-/// A trait to sign lightning channel transactions as described in BOLT 3.
-///
-/// Signing services could be implemented on a hardware wallet. In this case,
-/// the current Sign would be a front-end on top of a communication
-/// channel connected to your secure device and lightning key material wouldn't
-/// reside on a hot server. Nevertheless, a this deployment would still need
-/// to trust the ChannelManager to avoid loss of funds as this latest component
-/// could ask to sign commitment transaction with HTLCs paying to attacker pubkeys.
-///
-/// A more secure iteration would be to use hashlock (or payment points) to pair
-/// invoice/incoming HTLCs with outgoing HTLCs to implement a no-trust-ChannelManager
-/// at the price of more state and computation on the hardware wallet side. In the future,
-/// we are looking forward to design such interface.
+/// A trait to sign Lightning channel transactions as described in
+/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
///
-/// In any case, ChannelMonitor or fallback watchtowers are always going to be trusted
-/// to act, as liveness and breach reply correctness are always going to be hard requirements
-/// of LN security model, orthogonal of key management issues.
-// TODO: We should remove Clone by instead requesting a new Sign copy when we create
-// ChannelMonitors instead of expecting to clone the one out of the Channel into the monitors.
+/// Signing services could be implemented on a hardware wallet and should implement signing
+/// policies in order to be secure. Please refer to the [VLS Policy
+/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
+/// for an example of such policies.
pub trait BaseSign {
/// Gets the per-commitment point for a specific commitment number
///
- /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
+ /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey;
/// Gets the commitment secret for a specific commitment number as part of the revocation process
///
///
/// May be called more than once for the same index.
///
- /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
+ /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
// TODO: return a Result so we can signal a validation error
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
/// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
/// A validating signer should ensure that an HTLC output is removed only when the matching
/// preimage is provided, or when the value to holder is restored.
///
- /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// Note that all the relevant preimages will be provided, but there may also be additional
/// irrelevant or duplicate preimages.
- fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
- /// Gets the holder's channel public keys and basepoints
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction,
+ preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
+ /// Returns the holder's channel public keys and basepoints.
fn pubkeys(&self) -> &ChannelPublicKeys;
- /// Gets an arbitrary identifier describing the set of keys which are provided back to you in
- /// some SpendableOutputDescriptor types. This should be sufficient to identify this
- /// Sign object uniquely and lookup or re-derive its keys.
+ /// Returns an arbitrary identifier describing the set of keys which are provided back to you in
+ /// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this
+ /// [`BaseSign`] object uniquely and lookup or re-derive its keys.
fn channel_keys_id(&self) -> [u8; 32];
-
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
///
/// Note that if signing fails or is rejected, the channel will be force-closed.
/// A validating signer should ensure that an HTLC output is removed only when the matching
/// preimage is provided, or when the value to holder is restored.
///
- /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// Note that all the relevant preimages will be provided, but there may also be additional
/// irrelevant or duplicate preimages.
//
// TODO: Document the things someone using this interface should enforce before signing.
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction,
+ preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<(Signature, Vec<Signature>), ()>;
/// Validate the counterparty's revocation.
///
/// This is required in order for the signer to make sure that the state has moved
/// forward and it is safe to sign the next counterparty commitment.
fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
-
- /// Create a signatures for a holder's commitment transaction and its claiming HTLC transactions.
- /// This will only ever be called with a non-revoked commitment_tx. This will be called with the
- /// latest commitment_tx when we initiate a force-close.
- /// This will be called with the previous latest, just to get claiming HTLC signatures, if we are
- /// reacting to a ChannelMonitor replica that decided to broadcast before it had been updated to
- /// the latest.
+ /// Creates a signature for a holder's commitment transaction and its claiming HTLC transactions.
+ ///
+ /// This will be called
+ /// - with a non-revoked `commitment_tx`.
+ /// - with the latest `commitment_tx` when we initiate a force-close.
+ /// - with the previous `commitment_tx`, just to get claiming HTLC
+ /// signatures, if we are reacting to a [`ChannelMonitor`]
+ /// [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas)
+ /// that decided to broadcast before it had been updated to the latest `commitment_tx`.
+ ///
/// This may be called multiple times for the same transaction.
///
/// An external signer implementation should check that the commitment has not been revoked.
///
- /// May return Err if key derivation fails. Callers, such as ChannelMonitor, will panic in such a case.
- //
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
// TODO: Document the things someone using this interface should enforce before signing.
- // TODO: Key derivation failure should panic rather than Err
- fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
-
- /// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
- /// transactions which will be broadcasted later, after the channel has moved on to a newer
- /// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
- /// get called once.
+ fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ /// Same as [`sign_holder_commitment_and_htlcs`], but exists only for tests to get access to
+ /// holder commitment transactions which will be broadcasted later, after the channel has moved
+ /// on to a newer state. Thus, needs its own method as [`sign_holder_commitment_and_htlcs`] may
+ /// enforce that we only ever get called once.
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
- fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
-
+ fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
/// Create a signature for the given input in a transaction spending an HTLC transaction output
/// or a commitment transaction `to_local` output when our counterparty broadcasts an old state.
///
///
/// Amount is value of the output spent by this input, committed to in the BIP 143 signature.
///
- /// per_commitment_key is revocation secret which was provided by our counterparty when they
+ /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
/// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
- /// not allow the spending of any funds by itself (you need our holder revocation_secret to do
+ /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do
/// so).
- fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
-
+ fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
/// Create a signature for the given input in a transaction spending a commitment transaction
/// HTLC output when our counterparty broadcasts an old state.
///
/// It may be called multiple times for same output(s) if a fee-bump is needed with regards
/// to an upcoming timelock expiration.
///
- /// Amount is value of the output spent by this input, committed to in the BIP 143 signature.
+ /// `amount` is the value of the output spent by this input, committed to in the BIP 143
+ /// signature.
///
- /// per_commitment_key is revocation secret which was provided by our counterparty when they
+ /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
/// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
/// not allow the spending of any funds by itself (you need our holder revocation_secret to do
/// so).
///
- /// htlc holds HTLC elements (hash, timelock), thus changing the format of the witness script
+ /// `htlc` holds HTLC elements (hash, timelock), thus changing the format of the witness script
/// (which is committed to in the BIP 143 signatures).
- fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
-
+ fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ #[cfg(anchors)]
+ /// Computes the signature for a commitment transaction's HTLC output used as an input within
+ /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned
+ /// must be be computed using [`EcdsaSighashType::All`]. Note that this should only be used to
+ /// sign HTLC transactions from channels supporting anchor outputs after all additional
+ /// inputs/outputs have been added to the transaction.
+ ///
+ /// [`EcdsaSighashType::All`]: bitcoin::blockdata::transaction::EcdsaSighashType::All
+ fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize,
+ htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
/// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment
/// transaction, either offered or received.
///
/// signed for here. It may be called multiple times for same output(s) if a fee-bump is
/// needed with regards to an upcoming timelock expiration.
///
- /// Witness_script is either a offered or received script as defined in BOLT3 for HTLC
+ /// `witness_script` is either an offered or received script as defined in BOLT3 for HTLC
/// outputs.
///
- /// Amount is value of the output spent by this input, committed to in the BIP 143 signature.
+ /// `amount` is value of the output spent by this input, committed to in the BIP 143 signature.
///
- /// Per_commitment_point is the dynamic point corresponding to the channel state
+ /// `per_commitment_point` is the dynamic point corresponding to the channel state
/// detected onchain. It has been generated by our counterparty and is used to derive
/// channel state keys, which are then included in the witness script and committed to in the
/// BIP 143 signature.
- fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
-
+ fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
/// Create a signature for a (proposed) closing transaction.
///
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
/// chosen to forgo their output as dust.
- fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
-
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
/// Computes the signature for a commitment transaction's anchor output used as an
/// input within `anchor_tx`, which spends the commitment transaction, at index `input`.
fn sign_holder_anchor_input(
&self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
) -> Result<Signature, ()>;
-
/// Signs a channel announcement message with our funding key and our node secret key (aka
/// node_id or network_key), proving it comes from one of the channel participants.
///
/// protocol.
fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
-> Result<(Signature, Signature), ()>;
-
/// Set the counterparty static channel data, including basepoints,
- /// counterparty_selected/holder_selected_contest_delay and funding outpoint.
- /// This is done as soon as the funding outpoint is known. Since these are static channel data,
- /// they MUST NOT be allowed to change to different values once set.
+ /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint. Since these
+ /// are static channel data, they MUST NOT be allowed to change to different values once set,
+ /// as LDK may call this method more than once.
///
/// channel_parameters.is_populated() MUST be true.
- ///
- /// We bind holder_selected_contest_delay late here for API convenience.
- ///
- /// Will be called before any signatures are applied.
- fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters);
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters);
}
-/// A cloneable signer.
+/// A writeable signer.
///
-/// Although we require signers to be cloneable, it may be useful for developers to be able to use
-/// signers in an un-sized way, for example as `dyn BaseSign`. Therefore we separate the Clone trait,
-/// which implies Sized, into this derived trait.
-pub trait Sign: BaseSign + Writeable + Clone {
-}
+/// There will always be two instances of a signer per channel, one occupied by the
+/// [`ChannelManager`] and another by the channel's [`ChannelMonitor`].
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+pub trait Sign: BaseSign + Writeable {}
-/// Specifies the recipient of an invoice, to indicate to [`KeysInterface::sign_invoice`] what node
-/// secret key should be used to sign the invoice.
+/// Specifies the recipient of an invoice.
+///
+/// This indicates to [`KeysInterface::sign_invoice`] what node secret key should be used to sign
+/// the invoice.
pub enum Recipient {
/// The invoice should be signed with the local node secret key.
Node,
/// A trait to describe an object which can get user secrets and key material.
pub trait KeysInterface {
- /// A type which implements Sign which will be returned by get_channel_signer.
+ /// A type which implements [`Sign`] which will be returned by [`Self::derive_channel_signer`].
type Signer : Sign;
-
/// Get node secret key based on the provided [`Recipient`].
///
- /// The node_id/network_key is the public key that corresponds to this secret key.
+ /// The `node_id`/`network_key` is the public key that corresponds to this secret key.
///
- /// This method must return the same value each time it is called with a given `Recipient`
+ /// This method must return the same value each time it is called with a given [`Recipient`]
/// parameter.
///
- /// Errors if the `Recipient` variant is not supported by the implementation.
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()>;
/// Get node id based on the provided [`Recipient`]. This public key corresponds to the secret in
/// [`get_node_secret`].
///
- /// This method must return the same value each time it is called with a given `Recipient`
+ /// This method must return the same value each time it is called with a given [`Recipient`]
/// parameter.
///
- /// Errors if the `Recipient` variant is not supported by the implementation.
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
///
- /// [`get_node_secret`]: KeysInterface::get_node_secret
+ /// [`get_node_secret`]: Self::get_node_secret
fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
let secp_ctx = Secp256k1::signing_only();
Ok(PublicKey::from_secret_key(&secp_ctx, &self.get_node_secret(recipient)?))
/// one is provided. Note that this tweak can be applied to `other_key` instead of our node
/// secret, though this is less efficient.
///
- /// Errors if the `Recipient` variant is not supported by the implementation.
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
///
/// [`node secret`]: Self::get_node_secret
fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>;
/// This method should return a different value each time it is called, to avoid linking
/// on-chain funds across channels as controlled to the same user.
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript;
- /// Get a new set of Sign for per-channel secrets. These MUST be unique even if you
+ /// Get a new set of [`Sign`] for per-channel secrets. These MUST be unique even if you
/// restarted with some stale data!
///
/// This method must return a different value each time it is called.
- fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> Self::Signer;
+ fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32];
+ /// Derives the private key material backing a `Signer`.
+ ///
+ /// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through
+ /// [`KeysInterface::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be
+ /// re-derived from its `channel_keys_id`, which can be obtained through its trait method
+ /// [`BaseSign::channel_keys_id`].
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer;
/// Gets a unique, cryptographically-secure, random 32 byte value. This is used for encrypting
/// onion packets and for temporary channel IDs. There is no requirement that these be
/// persisted anywhere, though they must be unique across restarts.
///
/// This method must return a different value each time it is called.
fn get_secure_random_bytes(&self) -> [u8; 32];
-
- /// Reads a `Signer` for this `KeysInterface` from the given input stream.
+ /// Reads a [`Signer`] for this [`KeysInterface`] from the given input stream.
/// This is only called during deserialization of other objects which contain
- /// `Sign`-implementing objects (ie `ChannelMonitor`s and `ChannelManager`s).
+ /// [`Sign`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
/// The bytes are exactly those which `<Self::Signer as Writeable>::write()` writes, and
/// contain no versioning scheme. You may wish to include your own version prefix and ensure
/// you've read all of the provided bytes to ensure no corruption occurred.
+ ///
+ /// This method is slowly being phased out -- it will only be called when reading objects
+ /// written by LDK versions prior to 0.0.113.
+ ///
+ /// [`Signer`]: Self::Signer
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
-
/// Sign an invoice.
/// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of
/// this trait to parse the invoice and make sure they're signing what they expect, rather than
/// blindly signing the hash.
- /// The hrp is ascii bytes, while the invoice data is base32.
+ /// The `hrp` is ASCII bytes, while the invoice data is base32-encoded.
///
/// The secret key used to sign the invoice is dependent on the [`Recipient`].
///
- /// Errors if the `Recipient` variant is not supported by the implementation.
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], receipient: Recipient) -> Result<RecoverableSignature, ()>;
-
/// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
///
/// If the implementor of this trait supports [phantom node payments], then every node that is
/// intended to be included in the phantom invoice route hints must return the same value from
/// this method.
- // This is because LDK avoids storing inbound payment data by encrypting payment data in the
- // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
- // nodes, they must share the key that encrypts this payment data.
+ // This is because LDK avoids storing inbound payment data by encrypting payment data in the
+ // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
+ // nodes, they must share the key that encrypts this payment data.
///
/// This method must return the same value each time it is called.
///
}
#[derive(Clone)]
-/// A simple implementation of Sign that just keeps the private keys in memory.
+/// A simple implementation of [`Sign`] that just keeps the private keys in memory.
///
/// This implementation performs no policy checks and is insufficient by itself as
/// a secure external signer.
pub struct InMemorySigner {
- /// Private key of anchor tx
+ /// Holder secret key in the 2-of-2 multisig script of a channel. This key also backs the
+ /// holder's anchor output in a commitment transaction, if one is present.
pub funding_key: SecretKey,
- /// Holder secret key for blinded revocation pubkey
+ /// Holder secret key for blinded revocation pubkey.
pub revocation_base_key: SecretKey,
- /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions
+ /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions.
pub payment_key: SecretKey,
- /// Holder secret key used in HTLC tx
+ /// Holder secret key used in an HTLC transaction.
pub delayed_payment_base_key: SecretKey,
- /// Holder htlc secret key used in commitment tx htlc outputs
+ /// Holder HTLC secret key used in commitment transaction HTLC outputs.
pub htlc_base_key: SecretKey,
- /// Commitment seed
+ /// Commitment seed.
pub commitment_seed: [u8; 32],
- /// Holder public keys and basepoints
+ /// Holder public keys and basepoints.
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
- /// Private key of our node secret, used for signing channel announcements
+ /// Private key of our node secret, used for signing channel announcements.
node_secret: SecretKey,
- /// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
+ /// Counterparty public keys and counterparty/holder `selected_contest_delay`, populated on channel acceptance.
channel_parameters: Option<ChannelTransactionParameters>,
- /// The total value of this channel
+ /// The total value of this channel.
channel_value_satoshis: u64,
- /// Key derivation parameters
+ /// Key derivation parameters.
channel_keys_id: [u8; 32],
}
impl InMemorySigner {
- /// Create a new InMemorySigner
+ /// Creates a new [`InMemorySigner`].
pub fn new<C: Signing>(
secp_ctx: &Secp256k1<C>,
node_secret: SecretKey,
) -> InMemorySigner {
let holder_channel_pubkeys =
InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key,
- &payment_key, &delayed_payment_base_key,
- &htlc_base_key);
+ &payment_key, &delayed_payment_base_key,
+ &htlc_base_key);
InMemorySigner {
funding_key,
revocation_base_key,
}
fn make_holder_keys<C: Signing>(secp_ctx: &Secp256k1<C>,
- funding_key: &SecretKey,
- revocation_base_key: &SecretKey,
- payment_key: &SecretKey,
- delayed_payment_base_key: &SecretKey,
- htlc_base_key: &SecretKey) -> ChannelPublicKeys {
+ funding_key: &SecretKey,
+ revocation_base_key: &SecretKey,
+ payment_key: &SecretKey,
+ delayed_payment_base_key: &SecretKey,
+ htlc_base_key: &SecretKey) -> ChannelPublicKeys {
let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s);
ChannelPublicKeys {
funding_pubkey: from_secret(&funding_key),
}
}
- /// Counterparty pubkeys.
- /// Will panic if ready_channel wasn't called.
+ /// Returns the counterparty's pubkeys.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
-
- /// The contest_delay value specified by our counterparty and applied on holder-broadcastable
- /// transactions, ie the amount of time that we have to wait to recover our funds if we
+ /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
+ /// transactions, i.e., the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction.
- /// Will panic if ready_channel wasn't called.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
-
- /// The contest_delay value specified by us and applied on transactions broadcastable
- /// by our counterparty, ie the amount of time that they have to wait to recover their funds
+ /// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
+ /// by our counterparty, i.e., the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
- /// Will panic if ready_channel wasn't called.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
-
- /// Whether the holder is the initiator
- /// Will panic if ready_channel wasn't called.
+ /// Returns whether the holder is the initiator.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
-
/// Funding outpoint
- /// Will panic if ready_channel wasn't called.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
-
- /// Obtain a ChannelTransactionParameters for this channel, to be used when verifying or
+ /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or
/// building transactions.
///
- /// Will panic if ready_channel wasn't called.
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
self.channel_parameters.as_ref().unwrap()
}
-
- /// Whether anchors should be used.
- /// Will panic if ready_channel wasn't called.
+ /// Returns whether anchors should be used.
+ ///
+ /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
pub fn opt_anchors(&self) -> bool {
self.get_channel_parameters().opt_anchors.is_some()
}
-
- /// Sign the single input of spend_tx at index `input_idx` which spends the output
- /// described by descriptor, returning the witness stack for the input.
+ /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described
+ /// by `descriptor`, returning the witness stack for the input.
///
- /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
- /// is not spending the outpoint described by `descriptor.outpoint`,
- /// or if an output descriptor script_pubkey does not match the one we can spend.
+ /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
+ /// is not spending the outpoint described by [`descriptor.outpoint`],
+ /// or if an output descriptor `script_pubkey` does not match the one we can spend.
+ ///
+ /// [`descriptor.outpoint`]: StaticPaymentOutputDescriptor::outpoint
pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
// TODO: We really should be taking the SigHashCache as a parameter here instead of
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
let remotesig = sign(secp_ctx, &sighash, &self.payment_key);
let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
- if payment_script != descriptor.output.script_pubkey { return Err(()); }
+ if payment_script != descriptor.output.script_pubkey { return Err(()); }
let mut witness = Vec::with_capacity(2);
witness.push(remotesig.serialize_der().to_vec());
Ok(witness)
}
- /// Sign the single input of spend_tx at index `input_idx` which spends the output
- /// described by descriptor, returning the witness stack for the input.
+ /// Sign the single input of `spend_tx` at index `input_idx` which spends the output
+ /// described by `descriptor`, returning the witness stack for the input.
+ ///
+ /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
+ /// is not spending the outpoint described by [`descriptor.outpoint`], does not have a
+ /// sequence set to [`descriptor.to_self_delay`], or if an output descriptor
+ /// `script_pubkey` does not match the one we can spend.
///
- /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
- /// is not spending the outpoint described by `descriptor.outpoint`, does not have a
- /// sequence set to `descriptor.to_self_delay`, or if an output descriptor
- /// script_pubkey does not match the one we can spend.
+ /// [`descriptor.outpoint`]: DelayedPaymentOutputDescriptor::outpoint
+ /// [`descriptor.to_self_delay`]: DelayedPaymentOutputDescriptor::to_self_delay
pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
// TODO: We really should be taking the SigHashCache as a parameter here instead of
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); }
- let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key)
- .expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted.");
+ let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key);
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
witness.push(witness_script.clone().into_bytes());
Ok(witness)
}
-
}
impl BaseSign for InMemorySigner {
}
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
+
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
- let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
+ let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key);
htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
}
}
fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
let witness_script = {
- let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint).map_err(|_| ())?;
+ let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
};
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
}
fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
let witness_script = {
- let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint).map_err(|_| ())?;
- let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint).map_err(|_| ())?;
+ let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
+ let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
};
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
return Ok(sign(secp_ctx, &sighash, &revocation_key))
}
+ #[cfg(anchors)]
+ fn sign_holder_htlc_transaction(
+ &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
+ secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()> {
+ let per_commitment_point = self.get_per_commitment_point(
+ htlc_descriptor.per_commitment_number, &secp_ctx
+ );
+ let witness_script = htlc_descriptor.witness_script(&per_commitment_point, secp_ctx);
+ let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash(
+ input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, EcdsaSighashType::All
+ ).map_err(|_| ())?;
+ let our_htlc_private_key = chan_utils::derive_private_key(
+ &secp_ctx, &per_commitment_point, &self.htlc_base_key
+ );
+ Ok(sign(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key))
+ }
+
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key) {
- let witness_script = if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {
- if let Ok(counterparty_htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint) {
- if let Ok(htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint) {
- chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey)
- } else { return Err(()) }
- } else { return Err(()) }
- } else { return Err(()) };
- let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
- let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- return Ok(sign(secp_ctx, &sighash, &htlc_key))
- }
- Err(())
+ let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key);
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
+ let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
+ let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
+ let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
+ let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
+ Ok(sign(secp_ctx, &sighash, &htlc_key))
}
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key)))
}
- fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) {
- assert!(self.channel_parameters.is_none(), "Acceptance already noted");
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
+ assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters);
+ if self.channel_parameters.is_some() {
+ // The channel parameters were already set and they match, return early.
+ return;
+ }
assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated");
self.channel_parameters = Some(channel_parameters.clone());
}
}
const SERIALIZATION_VERSION: u8 = 1;
+
const MIN_SERIALIZATION_VERSION: u8 = 1;
impl Sign for InMemorySigner {}
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
InMemorySigner::make_holder_keys(&secp_ctx, &funding_key, &revocation_base_key,
- &payment_key, &delayed_payment_base_key,
- &htlc_base_key);
+ &payment_key, &delayed_payment_base_key, &htlc_base_key);
let keys_id = Readable::read(reader)?;
read_tlv_fields!(reader, {});
}
}
-/// Simple KeysInterface implementor that takes a 32-byte seed for use as a BIP 32 extended key
-/// and derives keys from that.
+/// Simple [`KeysInterface`] implementation that takes a 32-byte seed for use as a BIP 32 extended
+/// key and derives keys from that.
///
-/// Your node_id is seed/0'
-/// ChannelMonitor closes may use seed/1'
-/// Cooperative closes may use seed/2'
+/// Your `node_id` is seed/0'.
+/// Unilateral closes may use seed/1'.
+/// Cooperative closes may use seed/2'.
/// The two close keys may be needed to claim on-chain funds!
///
/// This struct cannot be used for nodes that wish to support receiving phantom payments;
}
impl KeysManager {
- /// Constructs a KeysManager from a 32-byte seed. If the seed is in some way biased (eg your
- /// CSRNG is busted) this may panic (but more importantly, you will possibly lose funds).
- /// starting_time isn't strictly required to actually be a time, but it must absolutely,
+ /// Constructs a [`KeysManager`] from a 32-byte seed. If the seed is in some way biased (e.g.,
+ /// your CSRNG is busted) this may panic (but more importantly, you will possibly lose funds).
+ /// `starting_time` isn't strictly required to actually be a time, but it must absolutely,
/// without a doubt, be unique to this instance. ie if you start multiple times with the same
- /// seed, starting_time must be unique to each run. Thus, the easiest way to achieve this is to
- /// simply use the current time (with very high precision).
+ /// `seed`, `starting_time` must be unique to each run. Thus, the easiest way to achieve this
+ /// is to simply use the current time (with very high precision).
///
- /// The seed MUST be backed up safely prior to use so that the keys can be re-created, however,
- /// obviously, starting_time should be unique every time you reload the library - it is only
+ /// The `seed` MUST be backed up safely prior to use so that the keys can be re-created, however,
+ /// obviously, `starting_time` should be unique every time you reload the library - it is only
/// used to generate new ephemeral key data (which will be stored by the individual channel if
/// necessary).
///
/// Note that the seed is required to recover certain on-chain funds independent of
- /// ChannelMonitor data, though a current copy of ChannelMonitor data is also required for any
- /// channel, and some on-chain during-closing funds.
+ /// [`ChannelMonitor`] data, though a current copy of [`ChannelMonitor`] data is also required
+ /// for any channel, and some on-chain during-closing funds.
///
- /// Note that until the 0.1 release there is no guarantee of backward compatibility between
- /// versions. Once the library is more fully supported, the docs will be updated to include a
- /// detailed description of the guarantee.
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
let secp_ctx = Secp256k1::new();
// Note that when we aren't serializing the key, network doesn't matter
Ok(destination_key) => {
let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes());
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0)
- .push_slice(&wpubkey_hash.into_inner())
- .into_script()
+ .push_slice(&wpubkey_hash.into_inner())
+ .into_script()
},
Err(_) => panic!("Your RNG is busted"),
};
inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]);
let mut rand_bytes_unique_start = Sha256::engine();
- rand_bytes_unique_start.input(&byte_utils::be64_to_array(starting_time_secs));
- rand_bytes_unique_start.input(&byte_utils::be32_to_array(starting_time_nanos));
+ rand_bytes_unique_start.input(&starting_time_secs.to_be_bytes());
+ rand_bytes_unique_start.input(&starting_time_nanos.to_be_bytes());
rand_bytes_unique_start.input(seed);
let mut res = KeysManager {
Err(_) => panic!("Your rng is busted"),
}
}
- /// Derive an old Sign containing per-channel secrets based on a key derivation parameters.
- ///
- /// Key derivation parameters are accessible through a per-channel secrets
- /// Sign::channel_keys_id and is provided inside DynamicOuputP2WSH in case of
- /// onchain output detection for which a corresponding delayed_payment_key must be derived.
+ /// Derive an old [`Sign`] containing per-channel secrets based on a key derivation parameters.
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
- let chan_id = byte_utils::slice_to_be64(¶ms[0..8]);
- assert!(chan_id <= core::u32::MAX as u64); // Otherwise the params field wasn't created by us
+ let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
let mut unique_start = Sha256::engine();
unique_start.input(params);
unique_start.input(&self.seed);
)
}
- /// Creates a Transaction which spends the given descriptors to the given outputs, plus an
+ /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
/// output to the given change destination (if sufficient change value remains). The
/// transaction will have a feerate, at least, of the given value.
///
///
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
///
- /// May panic if the `SpendableOutputDescriptor`s were not generated by Channels which used
- /// this KeysManager or one of the `InMemorySigner` created by this KeysManager.
+ /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
+ /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
let mut input = Vec::new();
let mut input_value = 0;
ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
}
- fn get_channel_signer(&self, _inbound: bool, channel_value_satoshis: u64) -> Self::Signer {
- let child_ix = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
- assert!(child_ix <= core::u32::MAX as usize);
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+ let child_idx = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
+ assert!(child_idx <= core::u32::MAX as usize);
let mut id = [0; 32];
- id[0..8].copy_from_slice(&byte_utils::be64_to_array(child_ix as u64));
- id[8..16].copy_from_slice(&byte_utils::be64_to_array(self.starting_time_nanos as u64));
- id[16..24].copy_from_slice(&byte_utils::be64_to_array(self.starting_time_secs));
- self.derive_channel_keys(channel_value_satoshis, &id)
+ id[0..4].copy_from_slice(&(child_idx as u32).to_be_bytes());
+ id[4..8].copy_from_slice(&self.starting_time_nanos.to_be_bytes());
+ id[8..16].copy_from_slice(&self.starting_time_secs.to_be_bytes());
+ id[16..32].copy_from_slice(&user_channel_id.to_be_bytes());
+ id
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
+ self.derive_channel_keys(channel_value_satoshis, &channel_keys_id)
}
fn get_secure_random_bytes(&self) -> [u8; 32] {
/// nodes in the case that one node goes down.
///
/// Note that multi-path payments are not supported in phantom invoices for security reasons.
-// In the hypothetical case that we did support MPP phantom payments, there would be no way for
-// nodes to know when the full payment has been received (and the preimage can be released) without
-// significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
-// to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
-// is released too early.
+// In the hypothetical case that we did support MPP phantom payments, there would be no way for
+// nodes to know when the full payment has been received (and the preimage can be released) without
+// significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
+// to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
+// is released too early.
//
/// Switching between this struct and [`KeysManager`] will invalidate any previously issued
/// invoices and attempts to pay previous invoices will fail.
self.inner.get_shutdown_scriptpubkey()
}
- fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> Self::Signer {
- self.inner.get_channel_signer(inbound, channel_value_satoshis)
+ fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+ self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
+ self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id)
}
fn get_secure_random_bytes(&self) -> [u8; 32] {
}
impl PhantomKeysManager {
- /// Constructs a `PhantomKeysManager` given a 32-byte seed and an additional `cross_node_seed`
- /// that is shared across all nodes that intend to participate in [phantom node payments] together.
+ /// Constructs a [`PhantomKeysManager`] given a 32-byte seed and an additional `cross_node_seed`
+ /// that is shared across all nodes that intend to participate in [phantom node payments]
+ /// together.
///
/// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and
/// `starting_time_nanos`.
fn block_disconnected(&self, header: &BlockHeader, height: u32);
}
-/// The `Confirm` trait is used to notify when transactions have been confirmed on chain or
-/// unconfirmed during a chain reorganization.
+/// The `Confirm` trait is used to notify LDK when relevant transactions have been confirmed on
+/// chain or unconfirmed during a chain reorganization.
///
/// Clients sourcing chain data using a transaction-oriented API should prefer this interface over
-/// [`Listen`]. For instance, an Electrum client may implement [`Filter`] by subscribing to activity
-/// related to registered transactions and outputs. Upon notification, it would pass along the
-/// matching transactions using this interface.
+/// [`Listen`]. For instance, an Electrum-based transaction sync implementation may implement
+/// [`Filter`] to subscribe to relevant transactions and unspent outputs it should monitor for
+/// on-chain activity. Then, it needs to notify LDK via this interface upon observing any changes
+/// with reference to the confirmation status of the monitored objects.
///
/// # Use
-///
/// The intended use is as follows:
-/// - Call [`transactions_confirmed`] to process any on-chain activity of interest.
-/// - Call [`transaction_unconfirmed`] to process any transaction returned by [`get_relevant_txids`]
-/// that has been reorganized out of the chain.
-/// - Call [`best_block_updated`] whenever a new chain tip becomes available.
+/// - Call [`transactions_confirmed`] to notify LDK whenever any of the registered transactions or
+/// outputs are, respectively, confirmed or spent on chain.
+/// - Call [`transaction_unconfirmed`] to notify LDK whenever any transaction returned by
+/// [`get_relevant_txids`] is no longer confirmed in the block with the given block hash.
+/// - Call [`best_block_updated`] to notify LDK whenever a new chain tip becomes available.
///
/// # Order
///
/// Clients must call these methods in chain order. Specifically:
-/// - Transactions confirmed in a block must be given before transactions confirmed in a later
-/// block.
+/// - Transactions which are confirmed in a particular block must be given before transactions
+/// confirmed in a later block.
/// - Dependent transactions within the same block must be given in topological order, possibly in
/// separate calls.
-/// - Unconfirmed transactions must be given after the original confirmations and before any
-/// reconfirmation.
+/// - All unconfirmed transactions must be given after the original confirmations and before *any*
+/// reconfirmations, i.e., [`transactions_confirmed`] and [`transaction_unconfirmed`] calls should
+/// never be interleaved, but always conduced *en bloc*.
+/// - Any reconfirmed transactions need to be explicitly unconfirmed before they are reconfirmed
+/// in regard to the new block.
///
/// See individual method documentation for further details.
///
/// [`best_block_updated`]: Self::best_block_updated
/// [`get_relevant_txids`]: Self::get_relevant_txids
pub trait Confirm {
- /// Processes transactions confirmed in a block with a given header and height.
+ /// Notifies LDK of transactions confirmed in a block with a given header and height.
///
- /// Should be called for any transactions registered by [`Filter::register_tx`] or any
+ /// Must be called for any transactions registered by [`Filter::register_tx`] or any
/// transactions spending an output registered by [`Filter::register_output`]. Such transactions
/// appearing in the same block do not need to be included in the same call; instead, multiple
/// calls with additional transactions may be made so long as they are made in [chain order].
/// [chain order]: Confirm#order
/// [`best_block_updated`]: Self::best_block_updated
fn transactions_confirmed(&self, header: &BlockHeader, txdata: &TransactionData, height: u32);
-
- /// Processes a transaction that is no longer confirmed as result of a chain reorganization.
+ /// Notifies LDK of a transaction that is no longer confirmed as result of a chain reorganization.
///
- /// Should be called for any transaction returned by [`get_relevant_txids`] if it has been
- /// reorganized out of the best chain. Once called, the given transaction will not be returned
+ /// Must be called for any transaction returned by [`get_relevant_txids`] if it has been
+ /// reorganized out of the best chain or if it is no longer confirmed in the block with the
+ /// given block hash. Once called, the given transaction will not be returned
/// by [`get_relevant_txids`], unless it has been reconfirmed via [`transactions_confirmed`].
///
/// [`get_relevant_txids`]: Self::get_relevant_txids
/// [`transactions_confirmed`]: Self::transactions_confirmed
fn transaction_unconfirmed(&self, txid: &Txid);
-
- /// Processes an update to the best header connected at the given height.
+ /// Notifies LDK of an update to the best header connected at the given height.
///
- /// Should be called when a new header is available but may be skipped for intermediary blocks
- /// if they become available at the same time.
+ /// Must be called whenever a new chain tip becomes available. May be skipped for intermediary
+ /// blocks.
fn best_block_updated(&self, header: &BlockHeader, height: u32);
-
- /// Returns transactions that should be monitored for reorganization out of the chain along
- /// with the hash of the block as part of which had been previously confirmed.
+ /// Returns transactions that must be monitored for reorganization out of the chain along
+ /// with the hash of the block as part of which it had been previously confirmed.
///
/// Will include any transactions passed to [`transactions_confirmed`] that have insufficient
/// confirmations to be safe from a chain reorganization. Will not include any transactions
/// passed to [`transaction_unconfirmed`], unless later reconfirmed.
///
- /// May be called to determine the subset of transactions that must still be monitored for
+ /// Must be called to determine the subset of transactions that must be monitored for
/// reorganization. Will be idempotent between calls but may change as a result of calls to the
- /// other interface methods. Thus, this is useful to determine which transactions may need to be
- /// given to [`transaction_unconfirmed`]. If any of the returned transactions are confirmed in
- /// a block other than the one with the given hash, they need to be unconfirmed and reconfirmed
- /// via [`transaction_unconfirmed`] and [`transactions_confirmed`], respectively.
+ /// other interface methods. Thus, this is useful to determine which transactions must be
+ /// given to [`transaction_unconfirmed`].
+ ///
+ /// If any of the returned transactions are confirmed in a block other than the one with the
+ /// given hash, they need to be unconfirmed and reconfirmed via [`transaction_unconfirmed`] and
+ /// [`transactions_confirmed`], respectively.
///
/// [`transactions_confirmed`]: Self::transactions_confirmed
/// [`transaction_unconfirmed`]: Self::transaction_unconfirmed
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1;
+use crate::chain::keysinterface::BaseSign;
use crate::ln::msgs::DecodeError;
use crate::ln::PaymentPreimage;
#[cfg(anchors)]
-use crate::ln::chan_utils;
+use crate::ln::chan_utils::{self, HTLCOutputInCommitment};
use crate::ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
#[cfg(anchors)]
use crate::chain::chaininterface::ConfirmationTarget;
use crate::chain::package::PackageTemplate;
use crate::util::logger::Logger;
use crate::util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, VecWriter};
-use crate::util::byte_utils;
use crate::io;
use crate::prelude::*;
/// Outpoint under claim process by our own tx, once this one get enough confirmations, we remove it from
/// bump-txn candidate buffer.
Claim {
- claim_request: Txid,
+ package_id: PackageID,
},
/// Claim tx aggregate multiple claimable outpoints. One of the outpoint may be claimed by a counterparty party tx.
/// In this case, we need to drop the outpoint and regenerate a new claim tx. By safety, we keep tracking
impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
(0, Claim) => {
- (0, claim_request, required),
+ (0, package_id, required),
},
(1, ContentiousOutpoint) => {
(0, package, required),
}
}
+#[cfg(anchors)]
+/// The claim commonly referred to as the pre-signed second-stage HTLC transaction.
+pub(crate) struct ExternalHTLCClaim {
+ pub(crate) commitment_txid: Txid,
+ pub(crate) per_commitment_number: u64,
+ pub(crate) htlc: HTLCOutputInCommitment,
+ pub(crate) preimage: Option<PaymentPreimage>,
+ pub(crate) counterparty_sig: Signature,
+}
+
// Represents the different types of claims for which events are yielded externally to satisfy said
// claims.
#[cfg(anchors)]
commitment_tx: Transaction,
anchor_output_idx: u32,
},
+ /// Event yielded to signal that the commitment transaction has confirmed and its HTLCs must be
+ /// resolved by broadcasting a transaction with sufficient fee to claim them.
+ BumpHTLC {
+ target_feerate_sat_per_1000_weight: u32,
+ htlcs: Vec<ExternalHTLCClaim>,
+ },
}
/// Represents the different ways an output can be claimed (i.e., spent to an address under our
Event(ClaimEvent),
}
+/// An internal identifier to track pending package claims within the `OnchainTxHandler`.
+type PackageID = [u8; 32];
+
/// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and
/// do RBF bumping if possible.
pub struct OnchainTxHandler<ChannelSigner: Sign> {
// us and is immutable until all outpoint of the claimable set are post-anti-reorg-delay solved.
// Entry is cache of elements need to generate a bumped claiming transaction (see ClaimTxBumpMaterial)
#[cfg(test)] // Used in functional_test to verify sanitization
- pub(crate) pending_claim_requests: HashMap<Txid, PackageTemplate>,
+ pub(crate) pending_claim_requests: HashMap<PackageID, PackageTemplate>,
#[cfg(not(test))]
- pending_claim_requests: HashMap<Txid, PackageTemplate>,
+ pending_claim_requests: HashMap<PackageID, PackageTemplate>,
#[cfg(anchors)]
- pending_claim_events: HashMap<Txid, ClaimEvent>,
+ pending_claim_events: HashMap<PackageID, ClaimEvent>,
// Used to link outpoints claimed in a connected block to a pending claim request.
// Key is outpoint than monitor parsing has detected we have keys/scripts to claim
// post-anti-reorg-delay solved, confirmaiton_block is used to erase entry if
// block with output gets disconnected.
#[cfg(test)] // Used in functional_test to verify sanitization
- pub claimable_outpoints: HashMap<BitcoinOutPoint, (Txid, u32)>,
+ pub claimable_outpoints: HashMap<BitcoinOutPoint, (PackageID, u32)>,
#[cfg(not(test))]
- claimable_outpoints: HashMap<BitcoinOutPoint, (Txid, u32)>,
+ claimable_outpoints: HashMap<BitcoinOutPoint, (PackageID, u32)>,
locktimed_packages: BTreeMap<u32, Vec<PackageTemplate>>,
(key_data.0.len() as u32).write(writer)?;
writer.write_all(&key_data.0[..])?;
- writer.write_all(&byte_utils::be64_to_array(self.pending_claim_requests.len() as u64))?;
+ writer.write_all(&(self.pending_claim_requests.len() as u64).to_be_bytes())?;
for (ref ancestor_claim_txid, request) in self.pending_claim_requests.iter() {
ancestor_claim_txid.write(writer)?;
request.write(writer)?;
}
- writer.write_all(&byte_utils::be64_to_array(self.claimable_outpoints.len() as u64))?;
+ writer.write_all(&(self.claimable_outpoints.len() as u64).to_be_bytes())?;
for (ref outp, ref claim_and_height) in self.claimable_outpoints.iter() {
outp.write(writer)?;
claim_and_height.0.write(writer)?;
claim_and_height.1.write(writer)?;
}
- writer.write_all(&byte_utils::be64_to_array(self.locktimed_packages.len() as u64))?;
+ writer.write_all(&(self.locktimed_packages.len() as u64).to_be_bytes())?;
for (ref locktime, ref packages) in self.locktimed_packages.iter() {
locktime.write(writer)?;
- writer.write_all(&byte_utils::be64_to_array(packages.len() as u64))?;
+ writer.write_all(&(packages.len() as u64).to_be_bytes())?;
for ref package in packages.iter() {
package.write(writer)?;
}
}
- writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?;
+ writer.write_all(&(self.onchain_events_awaiting_threshold_conf.len() as u64).to_be_bytes())?;
for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
entry.write(writer)?;
}
}
}
-impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
- fn read<R: io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
+impl<'a, K: KeysInterface> ReadableArgs<(&'a K, u64, [u8; 32])> for OnchainTxHandler<K::Signer> {
+ fn read<R: io::Read>(reader: &mut R, args: (&'a K, u64, [u8; 32])) -> Result<Self, DecodeError> {
+ let keys_manager = args.0;
+ let channel_value_satoshis = args.1;
+ let channel_keys_id = args.2;
+
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let destination_script = Readable::read(reader)?;
let channel_parameters = Readable::read(reader)?;
+ // Read the serialized signer bytes, but don't deserialize them, as we'll obtain our signer
+ // by re-deriving the private key material.
let keys_len: u32 = Readable::read(reader)?;
- let mut keys_data = Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE));
- while keys_data.len() != keys_len as usize {
+ let mut bytes_read = 0;
+ while bytes_read != keys_len as usize {
// Read 1KB at a time to avoid accidentally allocating 4GB on corrupted channel keys
let mut data = [0; 1024];
- let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - keys_data.len())];
+ let bytes_to_read = cmp::min(1024, keys_len as usize - bytes_read);
+ let read_slice = &mut data[0..bytes_to_read];
reader.read_exact(read_slice)?;
- keys_data.extend_from_slice(read_slice);
+ bytes_read += bytes_to_read;
}
- let signer = keys_manager.read_chan_signer(&keys_data)?;
+
+ let mut signer = keys_manager.derive_channel_signer(channel_value_satoshis, channel_keys_id);
+ signer.provide_channel_parameters(&channel_parameters);
let pending_claim_requests_len: u64 = Readable::read(reader)?;
let mut pending_claim_requests = HashMap::with_capacity(cmp::min(pending_claim_requests_len as usize, MAX_ALLOC_SIZE / 128));
// We check for outpoint spends within claims individually rather than as a set
// since requests can have outpoints split off.
if !self.onchain_events_awaiting_threshold_conf.iter()
- .any(|event_entry| if let OnchainEvent::Claim { claim_request } = event_entry.event {
- first_claim_txid_height.0 == claim_request
+ .any(|event_entry| if let OnchainEvent::Claim { package_id } = event_entry.event {
+ first_claim_txid_height.0 == package_id
} else {
// The onchain event is not a claim, keep seeking until we find one.
false
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
let new_timer = Some(cached_request.get_height_timer(cur_height));
if cached_request.is_malleable() {
+ #[cfg(anchors)]
+ { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+ if cached_request.requires_external_funding() {
+ let target_feerate_sat_per_1000_weight = cached_request
+ .compute_package_feerate(fee_estimator, ConfirmationTarget::HighPriority);
+ if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
+ return Some((
+ new_timer,
+ target_feerate_sat_per_1000_weight as u64,
+ OnchainClaim::Event(ClaimEvent::BumpHTLC {
+ target_feerate_sat_per_1000_weight,
+ htlcs,
+ }),
+ ));
+ } else {
+ return None;
+ }
+ }
+ }
+
let predicted_weight = cached_request.package_weight(&self.destination_script);
- if let Some((output_value, new_feerate)) =
- cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger) {
+ if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
+ predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger,
+ ) {
assert!(new_feerate != 0);
let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
assert!(predicted_weight >= transaction.weight());
- return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
+ return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
}
} else {
// Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
None
},
- });
+ })
}
None
}
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(cur_height, &req, &*fee_estimator, &*logger) {
req.set_timer(new_timer);
req.set_feerate(new_feerate);
- let txid = match claim {
+ let package_id = match claim {
OnchainClaim::Tx(tx) => {
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
broadcaster.broadcast_transaction(&tx);
- tx.txid()
+ tx.txid().into_inner()
},
#[cfg(anchors)]
OnchainClaim::Event(claim_event) => {
log_info!(logger, "Yielding onchain event to spend inputs {:?}", req.outpoints());
- let txid = match claim_event {
- ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
+ let package_id = match claim_event {
+ ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid().into_inner(),
+ ClaimEvent::BumpHTLC { ref htlcs, .. } => {
+ // Use the same construction as a lightning channel id to generate
+ // the package id for this request based on the first HTLC. It
+ // doesn't matter what we use as long as it's unique per request.
+ let mut package_id = [0; 32];
+ package_id[..].copy_from_slice(&htlcs[0].commitment_txid[..]);
+ let htlc_output_index = htlcs[0].htlc.transaction_output_index.unwrap();
+ package_id[30] ^= ((htlc_output_index >> 8) & 0xff) as u8;
+ package_id[31] ^= ((htlc_output_index >> 0) & 0xff) as u8;
+ package_id
+ },
};
- self.pending_claim_events.insert(txid, claim_event);
- txid
+ self.pending_claim_events.insert(package_id, claim_event);
+ package_id
},
};
for k in req.outpoints() {
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
- self.claimable_outpoints.insert(k.clone(), (txid, conf_height));
+ self.claimable_outpoints.insert(k.clone(), (package_id, conf_height));
}
- self.pending_claim_requests.insert(txid, req);
+ self.pending_claim_requests.insert(package_id, req);
}
}
}
//... we need to verify equality between transaction outpoints and claim request
// outpoints to know if transaction is the original claim or a bumped one issued
// by us.
- let mut set_equality = true;
- if request.outpoints().len() != tx.input.len() {
- set_equality = false;
- } else {
- for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
- if **claim_inp != tx_inp.previous_output {
- set_equality = false;
- }
+ let mut are_sets_equal = true;
+ let mut tx_inputs = tx.input.iter().map(|input| &input.previous_output).collect::<Vec<_>>();
+ tx_inputs.sort_unstable();
+ for request_input in request.outpoints() {
+ if tx_inputs.binary_search(&request_input).is_err() {
+ are_sets_equal = false;
+ break;
}
}
txid: tx.txid(),
height: conf_height,
block_hash: Some(conf_hash),
- event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() }
+ event: OnchainEvent::Claim { package_id: first_claim_txid_height.0 }
};
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
self.onchain_events_awaiting_threshold_conf.push(entry);
// If this is our transaction (or our counterparty spent all the outputs
// before we could anyway with same inputs order than us), wait for
// ANTI_REORG_DELAY and clean the RBF tracking map.
- if set_equality {
+ if are_sets_equal {
clean_claim_request_after_safety_delay!();
} else { // If false, generate new claim request with update outpoint set
let mut at_least_one_drop = false;
for entry in onchain_events_awaiting_threshold_conf {
if entry.has_reached_confirmation_threshold(cur_height) {
match entry.event {
- OnchainEvent::Claim { claim_request } => {
+ OnchainEvent::Claim { package_id } => {
// We may remove a whole set of claim outpoints here, as these one may have
// been aggregated in a single tx and claimed so atomically
- if let Some(request) = self.pending_claim_requests.remove(&claim_request) {
+ if let Some(request) = self.pending_claim_requests.remove(&package_id) {
for outpoint in request.outpoints() {
- log_debug!(logger, "Removing claim tracking for {} due to maturation of claim tx {}.", outpoint, claim_request);
+ log_debug!(logger, "Removing claim tracking for {} due to maturation of claim package {}.",
+ outpoint, log_bytes!(package_id));
self.claimable_outpoints.remove(&outpoint);
#[cfg(anchors)]
- self.pending_claim_events.remove(&claim_request);
+ self.pending_claim_events.remove(&package_id);
}
}
},
htlc_tx
}
+ #[cfg(anchors)]
+ pub(crate) fn generate_external_htlc_claim(
+ &self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
+ ) -> Option<ExternalHTLCClaim> {
+ let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<ExternalHTLCClaim> {
+ let trusted_tx = holder_commitment.trust();
+ if outp.txid != trusted_tx.txid() {
+ return None;
+ }
+ trusted_tx.htlcs().iter().enumerate()
+ .find(|(_, htlc)| if let Some(output_index) = htlc.transaction_output_index {
+ output_index == outp.vout
+ } else {
+ false
+ })
+ .map(|(htlc_idx, htlc)| {
+ let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
+ ExternalHTLCClaim {
+ commitment_txid: trusted_tx.txid(),
+ per_commitment_number: trusted_tx.commitment_number(),
+ htlc: htlc.clone(),
+ preimage: *preimage,
+ counterparty_sig: counterparty_htlc_sig,
+ }
+ })
+ };
+ // Check if the HTLC spends from the current holder commitment or the previous one otherwise.
+ find_htlc(&self.holder_commitment)
+ .or_else(|| self.prev_holder_commitment.as_ref().map(|c| find_htlc(c)).flatten())
+ }
+
pub(crate) fn opt_anchors(&self) -> bool {
self.channel_transaction_parameters.opt_anchors.is_some()
}
use crate::ln::msgs::DecodeError;
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
use crate::chain::keysinterface::Sign;
+#[cfg(anchors)]
+use crate::chain::onchaintx::ExternalHTLCClaim;
use crate::chain::onchaintx::OnchainTxHandler;
-use crate::util::byte_utils;
use crate::util::logger::Logger;
use crate::util::ser::{Readable, Writer, Writeable};
impl_writeable_tlv_based!(CounterpartyOfferedHTLCOutput, {
(0, per_commitment_point, required),
- (1, opt_anchors, option),
(2, counterparty_delayed_payment_base_key, required),
(4, counterparty_htlc_base_key, required),
(6, preimage, required),
(8, htlc, required),
+ (10, opt_anchors, option),
});
/// A struct to describe a HTLC output on a counterparty commitment transaction.
impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, {
(0, per_commitment_point, required),
- (1, opt_anchors, option),
(2, counterparty_delayed_payment_base_key, required),
(4, counterparty_htlc_base_key, required),
(6, htlc, required),
+ (8, opt_anchors, option),
});
/// A struct to describe a HTLC output on holder commitment transaction.
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct HolderHTLCOutput {
preimage: Option<PaymentPreimage>,
- amount: u64,
+ amount_msat: u64,
/// Defaults to 0 for HTLC-Success transactions, which have no expiry
cltv_expiry: u32,
+ opt_anchors: Option<()>,
}
impl HolderHTLCOutput {
- pub(crate) fn build_offered(amount: u64, cltv_expiry: u32) -> Self {
+ pub(crate) fn build_offered(amount_msat: u64, cltv_expiry: u32, opt_anchors: bool) -> Self {
HolderHTLCOutput {
preimage: None,
- amount,
+ amount_msat,
cltv_expiry,
+ opt_anchors: if opt_anchors { Some(()) } else { None } ,
}
}
- pub(crate) fn build_accepted(preimage: PaymentPreimage, amount: u64) -> Self {
+ pub(crate) fn build_accepted(preimage: PaymentPreimage, amount_msat: u64, opt_anchors: bool) -> Self {
HolderHTLCOutput {
preimage: Some(preimage),
- amount,
+ amount_msat,
cltv_expiry: 0,
+ opt_anchors: if opt_anchors { Some(()) } else { None } ,
}
}
+
+ fn opt_anchors(&self) -> bool {
+ self.opt_anchors.is_some()
+ }
}
impl_writeable_tlv_based!(HolderHTLCOutput, {
- (0, amount, required),
+ (0, amount_msat, required),
(2, cltv_expiry, required),
- (4, preimage, option)
+ (4, preimage, option),
+ (6, opt_anchors, option)
});
/// A struct to describe the channel output on the funding transaction.
impl_writeable_tlv_based!(HolderFundingOutput, {
(0, funding_redeemscript, required),
- (1, opt_anchors, option),
+ (2, opt_anchors, option),
(3, funding_amount, option),
});
PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.amount,
PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
- // Note: Currently, amounts of holder outputs spending witnesses aren't used
- // as we can't malleate spending package to increase their feerate. This
- // should change with the remaining anchor output patchset.
- PackageSolvingData::HolderHTLCOutput(..) => unreachable!(),
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ outp.amount_msat / 1000
+ },
PackageSolvingData::HolderFundingOutput(ref outp) => {
debug_assert!(outp.opt_anchors());
outp.funding_amount.unwrap()
amt
}
fn weight(&self) -> usize {
- let weight = match self {
- PackageSolvingData::RevokedOutput(ref outp) => { outp.weight as usize },
- PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.weight as usize },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { weight_offered_htlc(outp.opt_anchors()) as usize },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { weight_received_htlc(outp.opt_anchors()) as usize },
- // Note: Currently, weights of holder outputs spending witnesses aren't used
- // as we can't malleate spending package to increase their feerate. This
- // should change with the remaining anchor output patchset.
- PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() },
- PackageSolvingData::HolderFundingOutput(..) => { unreachable!() },
- };
- weight
+ match self {
+ PackageSolvingData::RevokedOutput(ref outp) => outp.weight as usize,
+ PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.weight as usize,
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => weight_offered_htlc(outp.opt_anchors()) as usize,
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => weight_received_htlc(outp.opt_anchors()) as usize,
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ if outp.preimage.is_none() {
+ weight_offered_htlc(true) as usize
+ } else {
+ weight_received_htlc(true) as usize
+ }
+ },
+ // Since HolderFundingOutput maps to an untractable package that is already signed, its
+ // weight can be determined from the transaction itself.
+ PackageSolvingData::HolderFundingOutput(..) => unreachable!(),
+ }
}
fn is_compatible(&self, input: &PackageSolvingData) -> bool {
match self {
fn finalize_input<Signer: Sign>(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler<Signer>) -> bool {
match self {
PackageSolvingData::RevokedOutput(ref outp) => {
- if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) {
- let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key);
- //TODO: should we panic on signer failure ?
- if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx) {
- let mut ser_sig = sig.serialize_der().to_vec();
- ser_sig.push(EcdsaSighashType::All as u8);
- bumped_tx.input[i].witness.push(ser_sig);
- bumped_tx.input[i].witness.push(vec!(1));
- bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
- } else { return false; }
- }
+ let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key);
+ //TODO: should we panic on signer failure ?
+ if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx) {
+ let mut ser_sig = sig.serialize_der().to_vec();
+ ser_sig.push(EcdsaSighashType::All as u8);
+ bumped_tx.input[i].witness.push(ser_sig);
+ bumped_tx.input[i].witness.push(vec!(1));
+ bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
+ } else { return false; }
},
PackageSolvingData::RevokedHTLCOutput(ref outp) => {
- if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) {
- let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
- //TODO: should we panic on signer failure ?
- if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_htlc(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &outp.htlc, &onchain_handler.secp_ctx) {
- let mut ser_sig = sig.serialize_der().to_vec();
- ser_sig.push(EcdsaSighashType::All as u8);
- bumped_tx.input[i].witness.push(ser_sig);
- bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec());
- bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
- } else { return false; }
- }
+ let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
+ //TODO: should we panic on signer failure ?
+ if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_htlc(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &outp.htlc, &onchain_handler.secp_ctx) {
+ let mut ser_sig = sig.serialize_der().to_vec();
+ ser_sig.push(EcdsaSighashType::All as u8);
+ bumped_tx.input[i].witness.push(ser_sig);
+ bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec());
+ bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
+ } else { return false; }
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => {
- if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) {
- let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
-
- if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) {
- let mut ser_sig = sig.serialize_der().to_vec();
- ser_sig.push(EcdsaSighashType::All as u8);
- bumped_tx.input[i].witness.push(ser_sig);
- bumped_tx.input[i].witness.push(outp.preimage.0.to_vec());
- bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
- }
+ let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
+
+ if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) {
+ let mut ser_sig = sig.serialize_der().to_vec();
+ ser_sig.push(EcdsaSighashType::All as u8);
+ bumped_tx.input[i].witness.push(ser_sig);
+ bumped_tx.input[i].witness.push(outp.preimage.0.to_vec());
+ bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
}
},
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => {
- if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) {
- let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
-
- bumped_tx.lock_time = PackedLockTime(outp.htlc.cltv_expiry); // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation
- if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) {
- let mut ser_sig = sig.serialize_der().to_vec();
- ser_sig.push(EcdsaSighashType::All as u8);
- bumped_tx.input[i].witness.push(ser_sig);
- // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
- bumped_tx.input[i].witness.push(vec![]);
- bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
- }
+ let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
+
+ bumped_tx.lock_time = PackedLockTime(outp.htlc.cltv_expiry); // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation
+ if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) {
+ let mut ser_sig = sig.serialize_der().to_vec();
+ ser_sig.push(EcdsaSighashType::All as u8);
+ bumped_tx.input[i].witness.push(ser_sig);
+ // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
+ bumped_tx.input[i].witness.push(vec![]);
+ bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
}
},
_ => { panic!("API Error!"); }
}
fn get_finalized_tx<Signer: Sign>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<Transaction> {
match self {
- PackageSolvingData::HolderHTLCOutput(ref outp) => { return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage); }
- PackageSolvingData::HolderFundingOutput(ref outp) => { return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript)); }
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(!outp.opt_anchors());
+ return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage);
+ }
+ PackageSolvingData::HolderFundingOutput(ref outp) => {
+ return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript));
+ }
_ => { panic!("API Error!"); }
}
}
let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
inputs_weight + witnesses_weight + transaction_weight + output_weight
}
+ #[cfg(anchors)]
+ pub(crate) fn construct_malleable_package_with_external_funding<Signer: Sign>(
+ &self, onchain_handler: &mut OnchainTxHandler<Signer>,
+ ) -> Option<Vec<ExternalHTLCClaim>> {
+ debug_assert!(self.requires_external_funding());
+ let mut htlcs: Option<Vec<ExternalHTLCClaim>> = None;
+ for (previous_output, input) in &self.inputs {
+ match input {
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ onchain_handler.generate_external_htlc_claim(&previous_output, &outp.preimage).map(|htlc| {
+ htlcs.get_or_insert_with(|| Vec::with_capacity(self.inputs.len())).push(htlc);
+ });
+ }
+ _ => debug_assert!(false, "Expected HolderHTLCOutputs to not be aggregated with other input types"),
+ }
+ }
+ htlcs
+ }
pub(crate) fn finalize_malleable_package<L: Deref, Signer: Sign>(
&self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L
) -> Option<Transaction> where L::Target: Logger {
pub(crate) fn requires_external_funding(&self) -> bool {
self.inputs.iter().find(|input| match input.1 {
PackageSolvingData::HolderFundingOutput(ref outp) => outp.opt_anchors(),
+ PackageSolvingData::HolderHTLCOutput(ref outp) => outp.opt_anchors(),
_ => false,
}).is_some()
}
PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable,
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable,
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::HolderHTLCOutput(..) => PackageMalleability::Untractable,
+ PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
+ PackageMalleability::Malleable
+ } else {
+ PackageMalleability::Untractable
+ },
PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable,
};
let mut inputs = Vec::with_capacity(1);
impl Writeable for PackageTemplate {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
- writer.write_all(&byte_utils::be64_to_array(self.inputs.len() as u64))?;
+ writer.write_all(&(self.inputs.len() as u64).to_be_bytes())?;
for (ref outpoint, ref rev_outp) in self.inputs.iter() {
outpoint.write(writer)?;
rev_outp.write(writer)?;
PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
- PackageSolvingData::HolderHTLCOutput(..) => { (PackageMalleability::Untractable, false) },
+ PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
+ (PackageMalleability::Malleable, outp.preimage.is_some())
+ } else {
+ (PackageMalleability::Untractable, false)
+ },
PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
}
} else { return Err(DecodeError::InvalidValue); };
() => {
{
let preimage = PaymentPreimage([2;32]);
- PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0))
+ PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0, false))
}
}
}
use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
use crate::util::ser::{Readable, Writeable, Writer};
-use crate::util::{byte_utils, transaction_utils};
+use crate::util::transaction_utils;
use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
-use bitcoin::secp256k1::Error as SecpError;
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
use bitcoin::PublicKey as BitcoinPublicKey;
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
for &(ref secret, ref idx) in self.old_secrets.iter() {
writer.write_all(secret)?;
- writer.write_all(&byte_utils::be64_to_array(*idx))?;
+ writer.write_all(&idx.to_be_bytes())?;
}
write_tlv_fields!(writer, {});
Ok(())
/// Derives a per-commitment-transaction private key (eg an htlc key or delayed_payment key)
/// from the base secret and the per_commitment_point.
-///
-/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
-/// generated (ie our own).
-pub fn derive_private_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_secret: &SecretKey) -> Result<SecretKey, SecpError> {
+pub fn derive_private_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_secret: &SecretKey) -> SecretKey {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&PublicKey::from_secret_key(&secp_ctx, &base_secret).serialize());
let res = Sha256::from_engine(sha).into_inner();
base_secret.clone().add_tweak(&Scalar::from_be_bytes(res).unwrap())
+ .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
}
/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key)
/// from the base point and the per_commitment_key. This is the public equivalent of
/// derive_private_key - using only public keys to derive a public key instead of private keys.
-///
-/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
-/// generated (ie our own).
-pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> Result<PublicKey, SecpError> {
+pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&base_point.serialize());
let res = Sha256::from_engine(sha).into_inner();
- let hashkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&res)?);
+ let hashkey = PublicKey::from_secret_key(&secp_ctx,
+ &SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken"));
base_point.combine(&hashkey)
+ .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
}
/// Derives a per-commitment-transaction revocation key from its constituent parts.
/// commitment transaction, thus per_commitment_secret always come from cheater
/// and revocation_base_secret always come from punisher, which is the broadcaster
/// of the transaction spending with this key knowledge.
-///
-/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
-/// generated (ie our own).
-pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_secret: &SecretKey, countersignatory_revocation_base_secret: &SecretKey) -> Result<SecretKey, SecpError> {
+pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>,
+ per_commitment_secret: &SecretKey, countersignatory_revocation_base_secret: &SecretKey)
+-> SecretKey {
let countersignatory_revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &countersignatory_revocation_base_secret);
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
Sha256::from_engine(sha).into_inner()
};
- let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
- let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
+ let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
+ .expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
+ let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
+ .expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
countersignatory_contrib.add_tweak(&Scalar::from_be_bytes(broadcaster_contrib.secret_bytes()).unwrap())
+ .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
}
/// Derives a per-commitment-transaction revocation public key from its constituent parts. This is
///
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
/// generated (ie our own).
-pub fn derive_public_revocation_key<T: secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, countersignatory_revocation_base_point: &PublicKey) -> Result<PublicKey, SecpError> {
+pub fn derive_public_revocation_key<T: secp256k1::Verification>(secp_ctx: &Secp256k1<T>,
+ per_commitment_point: &PublicKey, countersignatory_revocation_base_point: &PublicKey)
+-> PublicKey {
let rev_append_commit_hash_key = {
let mut sha = Sha256::engine();
sha.input(&countersignatory_revocation_base_point.serialize());
Sha256::from_engine(sha).into_inner()
};
- let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
- let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
+ let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
+ .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
+ let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
+ .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
countersignatory_contrib.combine(&broadcaster_contrib)
+ .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
}
/// The set of public keys which are used in the creation of one commitment transaction.
});
/// One counterparty's public keys which do not change over the life of a channel.
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelPublicKeys {
/// The public key which is used to sign all commitment transactions, as it appears in the
/// on-chain channel lock-in 2-of-2 multisig output.
impl TxCreationKeys {
/// Create per-state keys from channel base points and the per-commitment point.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
- pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
- Ok(TxCreationKeys {
+ pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> TxCreationKeys {
+ TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
- revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base)?,
- broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base)?,
- countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base)?,
- broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
- })
+ revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base),
+ broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base),
+ countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base),
+ broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base),
+ }
}
/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
- pub fn from_channel_static_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
+ pub fn from_channel_static_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> TxCreationKeys {
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
/// commitment transaction).
pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, opt_anchors: bool, use_non_zero_fee_anchors: bool, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction {
let mut txins: Vec<TxIn> = Vec::new();
- txins.push(TxIn {
+ txins.push(build_htlc_input(commitment_txid, htlc, opt_anchors));
+
+ let mut txouts: Vec<TxOut> = Vec::new();
+ txouts.push(build_htlc_output(
+ feerate_per_kw, contest_delay, htlc, opt_anchors, use_non_zero_fee_anchors,
+ broadcaster_delayed_payment_key, revocation_key
+ ));
+
+ Transaction {
+ version: 2,
+ lock_time: PackedLockTime(if htlc.offered { htlc.cltv_expiry } else { 0 }),
+ input: txins,
+ output: txouts,
+ }
+}
+
+pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommitment, opt_anchors: bool) -> TxIn {
+ TxIn {
previous_output: OutPoint {
txid: commitment_txid.clone(),
vout: htlc.transaction_output_index.expect("Can't build an HTLC transaction for a dust output"),
script_sig: Script::new(),
sequence: Sequence(if opt_anchors { 1 } else { 0 }),
witness: Witness::new(),
- });
+ }
+}
+pub(crate) fn build_htlc_output(
+ feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, opt_anchors: bool,
+ use_non_zero_fee_anchors: bool, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey
+) -> TxOut {
let weight = if htlc.offered {
htlc_timeout_tx_weight(opt_anchors)
} else {
htlc.amount_msat / 1000 - total_fee
};
- let mut txouts: Vec<TxOut> = Vec::new();
- txouts.push(TxOut {
+ TxOut {
script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_v0_p2wsh(),
value: output_value,
- });
+ }
+}
- Transaction {
- version: 2,
- lock_time: PackedLockTime(if htlc.offered { htlc.cltv_expiry } else { 0 }),
- input: txins,
- output: txouts,
+/// Returns the witness required to satisfy and spend a HTLC input.
+pub fn build_htlc_input_witness(
+ local_sig: &Signature, remote_sig: &Signature, preimage: &Option<PaymentPreimage>,
+ redeem_script: &Script, opt_anchors: bool,
+) -> Witness {
+ let remote_sighash_type = if opt_anchors {
+ EcdsaSighashType::SinglePlusAnyoneCanPay
+ } else {
+ EcdsaSighashType::All
+ };
+
+ let mut witness = Witness::new();
+ // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
+ witness.push(vec![]);
+ witness.push_bitcoin_signature(&remote_sig.serialize_der(), remote_sighash_type);
+ witness.push_bitcoin_signature(&local_sig.serialize_der(), EcdsaSighashType::All);
+ if let Some(preimage) = preimage {
+ witness.push(preimage.0.to_vec());
+ } else {
+ // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
+ witness.push(vec![]);
}
+ witness.push(redeem_script.to_bytes());
+ witness
}
/// Gets the witnessScript for the to_remote output when anchors are enabled.
/// Returns the witness required to satisfy and spend an anchor input.
pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
- let mut funding_sig = funding_sig.serialize_der().to_vec();
- funding_sig.push(EcdsaSighashType::All as u8);
- Witness::from_vec(vec![funding_sig, anchor_redeem_script.to_bytes()])
+ let mut ret = Witness::new();
+ ret.push_bitcoin_signature(&funding_sig.serialize_der(), EcdsaSighashType::All);
+ ret.push(anchor_redeem_script.as_bytes());
+ ret
}
/// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
///
/// Normally, this is converted to the broadcaster/countersignatory-organized DirectedChannelTransactionParameters
/// before use, via the as_holder_broadcastable and as_counterparty_broadcastable functions.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq)]
pub struct ChannelTransactionParameters {
/// Holder public keys
pub holder_pubkeys: ChannelPublicKeys,
}
/// Late-bound per-channel counterparty data used to build transactions.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq)]
pub struct CounterpartyChannelTransactionParameters {
/// Counter-party public keys
pub pubkeys: ChannelPublicKeys,
// First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
let mut tx = self.inner.built.transaction.clone();
tx.input[0].witness.push(Vec::new());
- let mut ser_holder_sig = holder_sig.serialize_der().to_vec();
- ser_holder_sig.push(EcdsaSighashType::All as u8);
- let mut ser_cp_sig = self.counterparty_sig.serialize_der().to_vec();
- ser_cp_sig.push(EcdsaSighashType::All as u8);
if self.holder_sig_first {
- tx.input[0].witness.push(ser_holder_sig);
- tx.input[0].witness.push(ser_cp_sig);
+ tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
+ tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
} else {
- tx.input[0].witness.push(ser_cp_sig);
- tx.input[0].witness.push(ser_holder_sig);
+ tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
+ tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
}
tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
pub fn verify<T: secp256k1::Signing + secp256k1::Verification>(&self, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TrustedCommitmentTransaction, ()> {
// This is the only field of the key cache that we trust
let per_commitment_point = self.keys.per_commitment_point;
- let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, broadcaster_keys, countersignatory_keys, secp_ctx).unwrap();
+ let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, broadcaster_keys, countersignatory_keys, secp_ctx);
if keys != self.keys {
return Err(());
}
let keys = &inner.keys;
let txid = inner.built.txid;
let mut ret = Vec::with_capacity(inner.htlcs.len());
- let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key).map_err(|_| ())?;
+ let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key);
for this_htlc in inner.htlcs.iter() {
assert!(this_htlc.transaction_output_index.is_some());
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
- let sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
-
- // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
- htlc_tx.input[0].witness.push(Vec::new());
-
- let mut cp_sig_ser = counterparty_signature.serialize_der().to_vec();
- cp_sig_ser.push(sighashtype as u8);
- htlc_tx.input[0].witness.push(cp_sig_ser);
- let mut holder_sig_ser = signature.serialize_der().to_vec();
- holder_sig_ser.push(EcdsaSighashType::All as u8);
- htlc_tx.input[0].witness.push(holder_sig_ser);
-
- if this_htlc.offered {
- // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
- htlc_tx.input[0].witness.push(Vec::new());
- } else {
- htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
- }
-
- htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
+ htlc_tx.input[0].witness = chan_utils::build_htlc_input_witness(
+ signature, counterparty_signature, preimage, &htlc_redeemscript, self.opt_anchors(),
+ );
htlc_tx
}
}
let seed = [42; 32];
let network = Network::Testnet;
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
- let signer = keys_provider.get_channel_signer(false, 3000);
- let counterparty_signer = keys_provider.get_channel_signer(false, 3000);
+ let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
+ let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
let delayed_payment_base = &signer.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 = &signer.pubkeys().htlc_basepoint;
let holder_pubkeys = signer.pubkeys();
let counterparty_pubkeys = counterparty_signer.pubkeys();
- let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint).unwrap();
+ let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
let mut channel_parameters = ChannelTransactionParameters {
holder_pubkeys: holder_pubkeys.clone(),
holder_selected_contest_delay: 0,
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, false, &keys).to_v0_p2wsh());
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, false, &keys).to_v0_p2wsh());
assert_eq!(get_htlc_redeemscript(&received_htlc, false, &keys).to_v0_p2wsh().to_hex(),
- "002085cf52e41ba7c099a39df504e7b61f6de122971ceb53b06731876eaeb85e8dc5");
+ "0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb");
assert_eq!(get_htlc_redeemscript(&offered_htlc, false, &keys).to_v0_p2wsh().to_hex(),
- "002049f0736bb335c61a04d2623a24df878a7592a3c51fa7258d41b2c85318265e73");
+ "0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
// Generate broadcaster output and received and offered HTLC outputs, with anchors
channel_parameters.opt_anchors = Some(());
assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, true, &keys).to_v0_p2wsh());
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, true, &keys).to_v0_p2wsh());
assert_eq!(get_htlc_redeemscript(&received_htlc, true, &keys).to_v0_p2wsh().to_hex(),
- "002067114123af3f95405bae4fd930fc95de03e3c86baaee8b2dd29b43dd26cf613c");
+ "0020b70d0649c72b38756885c7a30908d912a7898dd5d79457a7280b8e9a20f3f2bc");
assert_eq!(get_htlc_redeemscript(&offered_htlc, true, &keys).to_v0_p2wsh().to_hex(),
- "0020a06e3b0d4fcf704f2b9c41e16a70099e39989466c3142b8573a1154542f28f57");
+ "002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
}
#[test]
let mut w = test_utils::TestVecWriter(Vec::new());
monitor.write(&mut w).unwrap();
let new_monitor = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
- &mut io::Cursor::new(&w.0), &test_utils::OnlyReadsKeysInterface {}).unwrap().1;
+ &mut io::Cursor::new(&w.0), nodes[0].keys_manager).unwrap().1;
assert!(new_monitor == *monitor);
let chain_mon = test_utils::TestChainMonitor::new(Some(&chain_source), &tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator, &persister, &node_cfgs[0].keys_manager);
assert_eq!(chain_mon.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
let events_3 = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events_3.len(), 1);
match events_3[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(payment_hash_1, *payment_hash);
assert_eq!(amount_msat, 1_000_000);
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
let events_5 = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events_5.len(), 1);
match events_5[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(payment_hash_2, *payment_hash);
assert_eq!(amount_msat, 1_000_000);
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(payment_hash, our_payment_hash);
assert_eq!(amount_msat, 1_000_000);
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { payment_hash, .. } => {
+ Event::PaymentClaimable { payment_hash, .. } => {
assert_eq!(payment_hash, our_payment_hash);
},
_ => panic!("Unexpected event"),
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_second_raa);
check_added_monitors!(nodes[0], 1);
expect_pending_htlcs_forwardable!(nodes[0]);
- expect_payment_received!(nodes[0], our_payment_hash_2, our_payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[0], our_payment_hash_2, our_payment_secret_2, 1000000);
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_second_raa);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], our_payment_hash_1, our_payment_secret_1, 1000000);
+ expect_payment_claimable!(nodes[1], our_payment_hash_1, our_payment_secret_1, 1000000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_1);
claim_payment(&nodes[1], &[&nodes[0]], payment_preimage_2);
let events_6 = nodes[2].node.get_and_clear_pending_events();
assert_eq!(events_6.len(), 2);
match events_6[0] {
- Event::PaymentReceived { payment_hash, .. } => { assert_eq!(payment_hash, payment_hash_2); },
+ Event::PaymentClaimable { payment_hash, .. } => { assert_eq!(payment_hash, payment_hash_2); },
_ => panic!("Unexpected event"),
};
match events_6[1] {
- Event::PaymentReceived { payment_hash, .. } => { assert_eq!(payment_hash, payment_hash_3); },
+ Event::PaymentClaimable { payment_hash, .. } => { assert_eq!(payment_hash, payment_hash_3); },
_ => panic!("Unexpected event"),
};
let events_9 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_9.len(), 1);
match events_9[0] {
- Event::PaymentReceived { payment_hash, .. } => assert_eq!(payment_hash, payment_hash_4.unwrap()),
+ Event::PaymentClaimable { payment_hash, .. } => assert_eq!(payment_hash, payment_hash_4.unwrap()),
_ => panic!("Unexpected event"),
};
claim_payment(&nodes[2], &[&nodes[1], &nodes[0]], payment_preimage_4.unwrap());
check_added_monitors!(nodes[1], 0);
let bs_responses = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
// We send a third payment here, which is somewhat of a redundant test, but the
// chanmon_fail_consistency test required it to actually find the bug (by seeing out-of-sync
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_raa);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
let bs_update = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_raa);
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_raa);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_3, payment_secret_3, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_3, payment_secret_3, 1000000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_1);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_raa);
check_added_monitors!(nodes[0], 1);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_1);
}
check_added_monitors!(nodes[1], 0);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_1, payment_secret_1, 1000000);
let bs_responses = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_responses.0);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_1);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match events[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id } => {
assert_eq!(payment_hash_2, *payment_hash);
assert_eq!(1_000_000, amount_msat);
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
_ => panic!("Unexpected event"),
}
match events[1] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(payment_hash_3, *payment_hash);
assert_eq!(1_000_000, amount_msat);
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
_ => panic!("Unexpected event"),
};
nodes[0].node.process_pending_htlc_forwards();
- expect_payment_received!(nodes[0], payment_hash_2, payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[0], payment_hash_2, payment_secret_2, 1000000);
claim_payment(&nodes[2], &[&nodes[1], &nodes[0]], payment_preimage_2);
}
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_raa);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 1000000);
let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fulfill_htlcs[0]);
check_added_monitors!(nodes[1], 1);
expect_pending_htlcs_forwardable!(nodes[0]);
- expect_payment_received!(nodes[0], payment_hash, payment_secret, 1_000_000);
+ expect_payment_claimable!(nodes[0], payment_hash, payment_secret, 1_000_000);
claim_payment(&nodes[1], &[&nodes[0]], payment_preimage);
}
let as_revoke_and_ack = get_event_msg!(nodes[0], MessageSendEvent::SendRevokeAndACK, nodes[1].node.get_our_node_id());
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_revoke_and_ack);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_1, payment_secret_1, 100000);
+ expect_payment_claimable!(nodes[1], payment_hash_1, payment_secret_1, 100000);
check_added_monitors!(nodes[1], 1);
commitment_signed_dance!(nodes[1], nodes[0], (), false, true, false);
};
nodes[1].node.process_pending_htlc_forwards();
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 100000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 100000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_1);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
use crate::ln::msgs;
use crate::ln::msgs::{DecodeError, OptionalField, DataLossProtect};
use crate::ln::script::{self, ShutdownScript};
-use crate::ln::channelmanager::{self, CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
+use crate::ln::channelmanager::{self, CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSource, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
use crate::ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, htlc_success_tx_weight, htlc_timeout_tx_weight, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor, ClosingTransaction};
use crate::ln::chan_utils;
+use crate::ln::onion_utils::HTLCFailReason;
use crate::chain::BestBlock;
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator};
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::chain::keysinterface::{Sign, KeysInterface};
+use crate::chain::keysinterface::{Sign, KeysInterface, BaseSign};
use crate::util::events::ClosureReason;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
use crate::util::logger::Logger;
// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,
+
+ /// The unique identifier used to re-derive the private key material for the channel through
+ /// [`KeysInterface::derive_channel_signer`].
+ channel_keys_id: [u8; 32],
}
#[cfg(any(test, fuzzing))]
let opt_anchors = false; // TODO - should be based on features
let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
- let holder_signer = keys_provider.get_channel_signer(false, channel_value_satoshis);
+ let channel_keys_id = keys_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
+ let holder_signer = keys_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
let pubkeys = holder_signer.pubkeys().clone();
if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
historical_inbound_htlc_fulfills: HashSet::new(),
channel_type: Self::get_initial_channel_type(&config),
+ channel_keys_id,
})
}
return Err(ChannelError::Close("Channel Type was not understood - we require static remote key".to_owned()));
}
- let holder_signer = keys_provider.get_channel_signer(true, msg.funding_satoshis);
+ let channel_keys_id = keys_provider.generate_channel_keys_id(true, msg.funding_satoshis, user_id);
+ let holder_signer = keys_provider.derive_channel_signer(msg.funding_satoshis, channel_keys_id);
let pubkeys = holder_signer.pubkeys().clone();
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
historical_inbound_htlc_fulfills: HashSet::new(),
channel_type,
+ channel_keys_id,
};
Ok(chan)
/// our counterparty!)
/// 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) -> Result<TxCreationKeys, ChannelError> {
+ 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 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();
- Ok(secp_check!(TxCreationKeys::derive_new(&self.secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint), "Local tx keys generation got bogus keys".to_owned()))
+ TxCreationKeys::derive_new(&self.secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint)
}
#[inline]
/// Creates a set of keys for build_commitment_transaction to generate a transaction which we
/// will sign and send to our counterparty.
/// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
- fn build_remote_transaction_keys(&self) -> Result<TxCreationKeys, ChannelError> {
+ fn build_remote_transaction_keys(&self) -> TxCreationKeys {
//TODO: Ensure that the payment_key derived here ends up in the library users' wallet as we
//may see payments to it!
let revocation_basepoint = &self.get_holder_pubkeys().revocation_basepoint;
let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint;
let counterparty_pubkeys = self.get_counterparty_pubkeys();
- Ok(secp_check!(TxCreationKeys::derive_new(&self.secp_ctx, &self.counterparty_cur_commitment_point.unwrap(), &counterparty_pubkeys.delayed_payment_basepoint, &counterparty_pubkeys.htlc_basepoint, revocation_basepoint, htlc_basepoint), "Remote tx keys generation got bogus keys".to_owned()))
+ TxCreationKeys::derive_new(&self.secp_ctx, &self.counterparty_cur_commitment_point.unwrap(), &counterparty_pubkeys.delayed_payment_basepoint, &counterparty_pubkeys.htlc_basepoint, revocation_basepoint, htlc_basepoint)
}
/// Gets the redeemscript for the funding transaction output (ie the funding transaction output
/// an HTLC more than once or fulfill once and then attempt to fail after reconnect. We cannot,
/// however, fail more than once as we wait for an upstream failure to be irrevocably committed
/// before we fail backwards.
- /// If we do fail twice, we debug_assert!(false) and return Ok(None). Thus, will always return
- /// Ok(_) if debug assertions are turned on or preconditions are met.
- pub fn get_update_fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, logger: &L) -> Result<Option<msgs::UpdateFailHTLC>, ChannelError> where L::Target: Logger {
+ ///
+ /// If we do fail twice, we `debug_assert!(false)` and return `Ok(None)`. Thus, this will always
+ /// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
+ /// [`ChannelError::Ignore`].
+ pub fn queue_fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, logger: &L)
+ -> Result<(), ChannelError> where L::Target: Logger {
+ self.fail_htlc(htlc_id_arg, err_packet, true, logger)
+ .map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
+ }
+
+ /// We can only have one resolution per HTLC. In some cases around reconnect, we may fulfill
+ /// an HTLC more than once or fulfill once and then attempt to fail after reconnect. We cannot,
+ /// however, fail more than once as we wait for an upstream failure to be irrevocably committed
+ /// before we fail backwards.
+ ///
+ /// If we do fail twice, we `debug_assert!(false)` and return `Ok(None)`. Thus, this will always
+ /// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
+ /// [`ChannelError::Ignore`].
+ fn fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, mut force_holding_cell: bool, logger: &L)
+ -> Result<Option<msgs::UpdateFailHTLC>, ChannelError> where L::Target: Logger {
if (self.channel_state & (ChannelState::ChannelReady as u32)) != (ChannelState::ChannelReady as u32) {
panic!("Was asked to fail an HTLC when channel was not in an operational state");
}
return Ok(None);
}
- // Now update local state:
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateInProgress as u32)) != 0 {
+ debug_assert!(force_holding_cell, "!force_holding_cell is only called when emptying the holding cell, so we shouldn't end up back in it!");
+ force_holding_cell = true;
+ }
+
+ // Now update local state:
+ if force_holding_cell {
for pending_update in self.holding_cell_htlc_updates.iter() {
match pending_update {
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Txid, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
let funding_script = self.get_funding_redeemscript();
- let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?;
+ let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx;
{
let trusted_tx = initial_commitment_tx.trust();
secp_check!(self.secp_ctx.verify_ecdsa(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
}
- let counterparty_keys = self.build_remote_transaction_keys()?;
+ let counterparty_keys = self.build_remote_transaction_keys();
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
&self.get_counterparty_pubkeys().funding_pubkey
}
- pub fn funding_created<L: Deref>(&mut self, msg: &msgs::FundingCreated, best_block: BestBlock, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor<Signer>, Option<msgs::ChannelReady>), ChannelError> where L::Target: Logger {
+ pub fn funding_created<K: Deref, L: Deref>(
+ &mut self, msg: &msgs::FundingCreated, best_block: BestBlock, keys_source: &K, logger: &L
+ ) -> Result<(msgs::FundingSigned, ChannelMonitor<<K::Target as KeysInterface>::Signer>, Option<msgs::ChannelReady>), ChannelError>
+ where
+ K::Target: KeysInterface,
+ L::Target: Logger
+ {
if self.is_outbound() {
return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned()));
}
self.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.holder_signer.ready_channel(&self.channel_transaction_parameters);
+ self.holder_signer.provide_channel_parameters(&self.channel_transaction_parameters);
let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Ok(res) => res,
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound());
let shutdown_script = self.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
- let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(),
+ let mut monitor_signer = keys_source.derive_channel_signer(self.channel_value_satoshis, self.channel_keys_id);
+ monitor_signer.provide_channel_parameters(&self.channel_transaction_parameters);
+ let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), monitor_signer,
shutdown_script, self.get_holder_selected_contest_delay(),
&self.destination_script, (funding_txo, funding_txo_script.clone()),
&self.channel_transaction_parameters,
/// 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<L: Deref>(&mut self, msg: &msgs::FundingSigned, best_block: BestBlock, logger: &L) -> Result<(ChannelMonitor<Signer>, Transaction, Option<msgs::ChannelReady>), ChannelError> where L::Target: Logger {
+ pub fn funding_signed<K: Deref, L: Deref>(
+ &mut self, msg: &msgs::FundingSigned, best_block: BestBlock, keys_source: &K, logger: &L
+ ) -> Result<(ChannelMonitor<<K::Target as KeysInterface>::Signer>, Transaction, Option<msgs::ChannelReady>), ChannelError>
+ where
+ K::Target: KeysInterface,
+ L::Target: Logger
+ {
if !self.is_outbound() {
return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned()));
}
let funding_script = self.get_funding_redeemscript();
- let counterparty_keys = self.build_remote_transaction_keys()?;
+ let counterparty_keys = self.build_remote_transaction_keys();
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
log_bytes!(self.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
- let holder_signer = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?;
+ let holder_signer = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &holder_signer, true, false, logger).tx;
{
let trusted_tx = initial_commitment_tx.trust();
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound());
let shutdown_script = self.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
- let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(),
+ let mut monitor_signer = keys_source.derive_channel_signer(self.channel_value_satoshis, self.channel_keys_id);
+ monitor_signer.provide_channel_parameters(&self.channel_transaction_parameters);
+ let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), monitor_signer,
shutdown_script, self.get_holder_selected_contest_delay(),
&self.destination_script, (funding_txo, funding_txo_script),
&self.channel_transaction_parameters,
let funding_script = self.get_funding_redeemscript();
- let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number).map_err(|e| (None, e))?;
+ let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
let commitment_stats = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, false, logger);
let commitment_txid = {
} else { Ok((None, Vec::new())) }
}
- /// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
- /// fulfilling or failing the last pending HTLC)
+ /// Frees any pending commitment updates in the holding cell, generating the relevant messages
+ /// for our counterparty.
fn free_holding_cell_htlcs<L: Deref>(&mut self, logger: &L) -> Result<(Option<(msgs::CommitmentUpdate, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>), ChannelError> where L::Target: Logger {
assert_eq!(self.channel_state & ChannelState::MonitorUpdateInProgress as u32, 0);
if self.holding_cell_htlc_updates.len() != 0 || self.holding_cell_update_fee.is_some() {
// to rebalance channels.
match &htlc_update {
&HTLCUpdateAwaitingACK::AddHTLC {amount_msat, cltv_expiry, ref payment_hash, ref source, ref onion_routing_packet, ..} => {
- match self.send_htlc(amount_msat, *payment_hash, cltv_expiry, source.clone(), onion_routing_packet.clone(), logger) {
+ match self.send_htlc(amount_msat, *payment_hash, cltv_expiry, source.clone(), onion_routing_packet.clone(), false, logger) {
Ok(update_add_msg_option) => update_add_htlcs.push(update_add_msg_option.unwrap()),
Err(e) => {
match e {
monitor_update.updates.append(&mut additional_monitor_update.updates);
},
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, ref err_packet } => {
- match self.get_update_fail_htlc(htlc_id, err_packet.clone(), logger) {
+ match self.fail_htlc(htlc_id, err_packet.clone(), false, logger) {
Ok(update_fail_msg_option) => {
// If an HTLC failure was previously added to the holding cell (via
- // `get_update_fail_htlc`) then generating the fail message itself
- // must not fail - we should never end up in a state where we
- // double-fail an HTLC or fail-then-claim an HTLC as it indicates
- // we didn't wait for a full revocation before failing.
+ // `queue_fail_htlc`) then generating the fail message itself must
+ // not fail - we should never end up in a state where we double-fail
+ // an HTLC or fail-then-claim an HTLC as it indicates we didn't wait
+ // for a full revocation before failing.
update_fail_htlcs.push(update_fail_msg_option.unwrap())
},
Err(e) => {
return Ok((None, htlcs_to_fail));
}
let update_fee = if let Some(feerate) = self.holding_cell_update_fee.take() {
- self.send_update_fee(feerate, logger)
+ self.send_update_fee(feerate, false, logger)
} else {
None
};
}
}
+ /// Queues up an outbound update fee by placing it in the holding cell. You should call
+ /// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
+ /// commitment update.
+ pub fn queue_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, logger: &L) where L::Target: Logger {
+ let msg_opt = self.send_update_fee(feerate_per_kw, true, logger);
+ assert!(msg_opt.is_none(), "We forced holding cell?");
+ }
+
/// Adds a pending update to this channel. See the doc for send_htlc for
/// further details on the optionness of the return value.
/// If our balance is too low to cover the cost of the next commitment transaction at the
/// new feerate, the update is cancelled.
- /// You MUST call send_commitment prior to any other calls on this Channel
- fn send_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, logger: &L) -> Option<msgs::UpdateFee> where L::Target: Logger {
+ ///
+ /// You MUST call [`Self::send_commitment_no_state_update`] prior to any other calls on this
+ /// [`Channel`] if `force_holding_cell` is false.
+ fn send_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, mut force_holding_cell: bool, logger: &L) -> Option<msgs::UpdateFee> where L::Target: Logger {
if !self.is_outbound() {
panic!("Cannot send fee from inbound channel");
}
// Before proposing a feerate update, check that we can actually afford the new fee.
let inbound_stats = self.get_inbound_pending_htlc_stats(Some(feerate_per_kw));
let outbound_stats = self.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
- let keys = if let Ok(keys) = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number) { keys } else { return None; };
+ let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
let commitment_stats = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, true, logger);
let buffer_fee_msat = Channel::<Signer>::commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + outbound_stats.on_holder_tx_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.opt_anchors()) * 1000;
let holder_balance_msat = commitment_stats.local_balance_msat - outbound_stats.holding_cell_msat;
}
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateInProgress as u32)) != 0 {
+ force_holding_cell = true;
+ }
+
+ if force_holding_cell {
self.holding_cell_update_fee = Some(feerate_per_kw);
return None;
}
})
}
- pub fn send_update_fee_and_commit<L: Deref>(&mut self, feerate_per_kw: u32, logger: &L) -> Result<Option<(msgs::UpdateFee, msgs::CommitmentSigned, ChannelMonitorUpdate)>, ChannelError> where L::Target: Logger {
- match self.send_update_fee(feerate_per_kw, logger) {
- Some(update_fee) => {
- let (commitment_signed, monitor_update) = self.send_commitment_no_status_check(logger)?;
- Ok(Some((update_fee, commitment_signed, monitor_update)))
- },
- None => Ok(None)
- }
- }
-
/// Removes any uncommitted inbound HTLCs and resets the state of uncommitted outbound HTLC
/// updates, to be used on peer disconnection. After this, update_*_htlc messages need to be
/// resent.
/// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
fn get_outbound_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> where L::Target: Logger {
- let counterparty_keys = self.build_remote_transaction_keys()?;
+ let counterparty_keys = self.build_remote_transaction_keys();
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
}
self.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
- self.holder_signer.ready_channel(&self.channel_transaction_parameters);
+ self.holder_signer.provide_channel_parameters(&self.channel_transaction_parameters);
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
// Send stuff to our remote peers:
+ /// Queues up an outbound HTLC to send by placing it in the holding cell. You should call
+ /// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
+ /// commitment update.
+ ///
+ /// `Err`s will only be [`ChannelError::Ignore`].
+ pub fn queue_add_htlc<L: Deref>(&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource,
+ onion_routing_packet: msgs::OnionPacket, logger: &L)
+ -> Result<(), ChannelError> where L::Target: Logger {
+ self
+ .send_htlc(amount_msat, payment_hash, cltv_expiry, source, onion_routing_packet, true, logger)
+ .map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
+ .map_err(|err| {
+ if let ChannelError::Ignore(_) = err { /* fine */ }
+ else { debug_assert!(false, "Queueing cannot trigger channel failure"); }
+ err
+ })
+ }
+
/// Adds a pending outbound HTLC to this channel, note that you probably want
- /// send_htlc_and_commit instead cause you'll want both messages at once.
+ /// [`Self::send_htlc_and_commit`] instead cause you'll want both messages at once.
///
/// This returns an optional UpdateAddHTLC as we may be in a state where we cannot add HTLCs on
/// the wire:
/// we may not yet have sent the previous commitment update messages and will need to
/// regenerate them.
///
- /// You MUST call send_commitment prior to calling any other methods on this Channel!
+ /// You MUST call [`Self::send_commitment_no_state_update`] prior to calling any other methods
+ /// on this [`Channel`] if `force_holding_cell` is false.
///
- /// If an Err is returned, it's a ChannelError::Ignore!
- pub fn send_htlc<L: Deref>(&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource, onion_routing_packet: msgs::OnionPacket, logger: &L) -> Result<Option<msgs::UpdateAddHTLC>, ChannelError> where L::Target: Logger {
+ /// `Err`s will only be [`ChannelError::Ignore`].
+ fn send_htlc<L: Deref>(&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource,
+ onion_routing_packet: msgs::OnionPacket, mut force_holding_cell: bool, logger: &L)
+ -> Result<Option<msgs::UpdateAddHTLC>, ChannelError> where L::Target: Logger {
if (self.channel_state & (ChannelState::ChannelReady as u32 | BOTH_SIDES_SHUTDOWN_MASK)) != (ChannelState::ChannelReady as u32) {
return Err(ChannelError::Ignore("Cannot send HTLC until channel is fully established and we haven't started shutting down".to_owned()));
}
return Err(ChannelError::Ignore(format!("Cannot send value that would put us over the max HTLC value in flight our peer will accept ({})", self.counterparty_max_htlc_value_in_flight_msat)));
}
- let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?;
+ let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number);
let commitment_stats = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, true, logger);
if !self.is_outbound() {
// Check that we won't violate the remote channel reserve by adding this HTLC.
return Err(ChannelError::Ignore(format!("Cannot send value that would put our balance under counterparty-announced channel reserve value ({})", chan_reserve_msat)));
}
- // Now update local state:
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::MonitorUpdateInProgress as u32)) != 0 {
+ force_holding_cell = true;
+ }
+
+ // Now update local state:
+ if force_holding_cell {
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::AddHTLC {
amount_msat,
payment_hash,
Ok(Some(res))
}
- /// Creates a signed commitment transaction to send to the remote peer.
- /// Always returns a ChannelError::Close if an immediately-preceding (read: the
- /// last call to this Channel) send_htlc returned Ok(Some(_)) and there is an Err.
- /// May panic if called except immediately after a successful, Ok(Some(_))-returning send_htlc.
- pub fn send_commitment<L: Deref>(&mut self, logger: &L) -> Result<(msgs::CommitmentSigned, ChannelMonitorUpdate), ChannelError> where L::Target: Logger {
- if (self.channel_state & (ChannelState::ChannelReady as u32)) != (ChannelState::ChannelReady as u32) {
- panic!("Cannot create commitment tx until channel is fully established");
- }
- if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
- panic!("Cannot create commitment tx until remote revokes their previous commitment");
- }
- if (self.channel_state & (ChannelState::PeerDisconnected as u32)) == (ChannelState::PeerDisconnected as u32) {
- panic!("Cannot create commitment tx while disconnected, as send_htlc will have returned an Err so a send_commitment precondition has been violated");
- }
- if (self.channel_state & (ChannelState::MonitorUpdateInProgress as u32)) == (ChannelState::MonitorUpdateInProgress as u32) {
- panic!("Cannot create commitment tx while awaiting monitor update unfreeze, as send_htlc will have returned an Err so a send_commitment precondition has been violated");
- }
- let mut have_updates = self.is_outbound() && self.pending_update_fee.is_some();
- for htlc in self.pending_outbound_htlcs.iter() {
- if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
- have_updates = true;
- }
- if have_updates { break; }
- }
- for htlc in self.pending_inbound_htlcs.iter() {
- if let InboundHTLCState::LocalRemoved(_) = htlc.state {
- have_updates = true;
- }
- if have_updates { break; }
- }
- if !have_updates {
- panic!("Cannot create commitment tx until we have some updates to send");
- }
- self.send_commitment_no_status_check(logger)
- }
/// Only fails in case of bad keys
fn send_commitment_no_status_check<L: Deref>(&mut self, logger: &L) -> Result<(msgs::CommitmentSigned, ChannelMonitorUpdate), ChannelError> where L::Target: Logger {
log_trace!(logger, "Updating HTLC state for a newly-sent commitment_signed...");
/// Only fails in case of bad keys. Used for channel_reestablish commitment_signed generation
/// when we shouldn't change HTLC/channel state.
fn send_commitment_no_state_update<L: Deref>(&self, logger: &L) -> Result<(msgs::CommitmentSigned, (Txid, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>)), ChannelError> where L::Target: Logger {
- let counterparty_keys = self.build_remote_transaction_keys()?;
+ let counterparty_keys = self.build_remote_transaction_keys();
let commitment_stats = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger);
let counterparty_commitment_txid = commitment_stats.tx.trust().txid();
let (signature, htlc_signatures);
/// Adds a pending outbound HTLC to this channel, and creates a signed commitment transaction
/// to send to the remote peer in one go.
- /// Shorthand for calling send_htlc() followed by send_commitment(), see docs on those for
- /// more info.
+ ///
+ /// Shorthand for calling [`Self::send_htlc`] followed by a commitment update, see docs on
+ /// [`Self::send_htlc`] and [`Self::send_commitment_no_state_update`] for more info.
pub fn send_htlc_and_commit<L: Deref>(&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource, onion_routing_packet: msgs::OnionPacket, logger: &L) -> Result<Option<(msgs::UpdateAddHTLC, msgs::CommitmentSigned, ChannelMonitorUpdate)>, ChannelError> where L::Target: Logger {
- match self.send_htlc(amount_msat, payment_hash, cltv_expiry, source, onion_routing_packet, logger)? {
+ match self.send_htlc(amount_msat, payment_hash, cltv_expiry, source, onion_routing_packet, false, logger)? {
Some(update_add_htlc) => {
let (commitment_signed, monitor_update) = self.send_commitment_no_status_check(logger)?;
Ok(Some((update_add_htlc, commitment_signed, monitor_update)))
(monitor_update, dropped_outbound_htlcs)
}
- pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=&HTLCSource> {
+ pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=(&HTLCSource, &PaymentHash)> {
self.holding_cell_htlc_updates.iter()
.flat_map(|htlc_update| {
match htlc_update {
- HTLCUpdateAwaitingACK::AddHTLC { source, .. } => { Some(source) }
- _ => None
+ HTLCUpdateAwaitingACK::AddHTLC { source, payment_hash, .. }
+ => Some((source, payment_hash)),
+ _ => None,
}
})
- .chain(self.pending_outbound_htlcs.iter().map(|htlc| &htlc.source))
+ .chain(self.pending_outbound_htlcs.iter().map(|htlc| (&htlc.source, &htlc.payment_hash)))
}
}
-const SERIALIZATION_VERSION: u8 = 2;
+const SERIALIZATION_VERSION: u8 = 3;
const MIN_SERIALIZATION_VERSION: u8 = 2;
impl_writeable_tlv_based_enum!(InboundHTLCRemovalReason,;
// Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been
// called.
- write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
+ write_ver_prefix!(writer, MIN_SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
// `user_id` used to be a single u64 value. In order to remain backwards compatible with
// versions prior to 0.0.113, the u128 is serialized as two separate u64 values. We write
(21, self.outbound_scid_alias, required),
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
+ (27, self.channel_keys_id, required),
});
Ok(())
let latest_monitor_update_id = Readable::read(reader)?;
- let keys_len: u32 = Readable::read(reader)?;
- let mut keys_data = Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE));
- while keys_data.len() != keys_len as usize {
- // Read 1KB at a time to avoid accidentally allocating 4GB on corrupted channel keys
- let mut data = [0; 1024];
- let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - keys_data.len())];
- reader.read_exact(read_slice)?;
- keys_data.extend_from_slice(read_slice);
+ let mut keys_data = None;
+ if ver <= 2 {
+ // Read the serialize signer bytes. We'll choose to deserialize them or not based on whether
+ // the `channel_keys_id` TLV is present below.
+ let keys_len: u32 = Readable::read(reader)?;
+ keys_data = Some(Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE)));
+ while keys_data.as_ref().unwrap().len() != keys_len as usize {
+ // Read 1KB at a time to avoid accidentally allocating 4GB on corrupted channel keys
+ let mut data = [0; 1024];
+ let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - keys_data.as_ref().unwrap().len())];
+ reader.read_exact(read_slice)?;
+ keys_data.as_mut().unwrap().extend_from_slice(read_slice);
+ }
}
- let holder_signer = keys_source.read_chan_signer(&keys_data)?;
// Read the old serialization for shutdown_pubkey, preferring the TLV field later if set.
let mut shutdown_scriptpubkey = match <PublicKey as Readable>::read(reader) {
let mut channel_ready_event_emitted = None;
let mut user_id_high_opt: Option<u64> = None;
+ let mut channel_keys_id: Option<[u8; 32]> = None;
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(21, outbound_scid_alias, option),
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
+ (27, channel_keys_id, option),
});
+ let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
+ let mut holder_signer = keys_source.derive_channel_signer(channel_value_satoshis, channel_keys_id);
+ // If we've gotten to the funding stage of the channel, populate the signer with its
+ // required channel parameters.
+ let non_shutdown_state = channel_state & (!MULTI_STATE_FLAGS);
+ if non_shutdown_state >= (ChannelState::FundingCreated as u32) {
+ holder_signer.provide_channel_parameters(&channel_parameters);
+ }
+ (channel_keys_id, holder_signer)
+ } else {
+ // `keys_data` can be `None` if we had corrupted data.
+ let keys_data = keys_data.ok_or(DecodeError::InvalidValue)?;
+ let holder_signer = keys_source.read_chan_signer(&keys_data)?;
+ (holder_signer.channel_keys_id(), holder_signer)
+ };
+
if let Some(preimages) = preimages_opt {
let mut iter = preimages.into_iter();
for htlc in pending_outbound_htlcs.iter_mut() {
historical_inbound_htlc_fulfills,
channel_type: channel_type.unwrap(),
+ channel_keys_id,
})
}
}
use crate::ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight};
use crate::chain::BestBlock;
use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget};
- use crate::chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial, KeysInterface};
+ use crate::chain::keysinterface::{BaseSign, InMemorySigner, Recipient, KeyMaterial, KeysInterface};
use crate::chain::transaction::OutPoint;
use crate::util::config::UserConfig;
use crate::util::enforcing_trait_impls::EnforcingSigner;
ShutdownScript::new_p2wpkh_from_pubkey(PublicKey::from_secret_key(&secp_ctx, &channel_close_key))
}
- fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> InMemorySigner {
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] {
+ self.signer.channel_keys_id()
+ }
+ fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::Signer {
self.signer.clone()
}
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
let funding_created_msg = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).unwrap();
- let (funding_signed_msg, _, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&logger).unwrap();
+ let (funding_signed_msg, _, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&keys_provider, &&logger).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&logger);
+ let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&keys_provider, &&logger);
// Now disconnect the two nodes and check that the commitment point in
// Node B's channel_reestablish message is sane.
selected_contest_delay: 144
});
chan.channel_transaction_parameters.funding_outpoint = Some(funding_info);
- signer.ready_channel(&chan.channel_transaction_parameters);
+ signer.provide_channel_parameters(&chan.channel_transaction_parameters);
assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
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.holder_signer.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).unwrap();
+ 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 {
( $counterparty_sig_hex: expr, $sig_hex: expr, $tx_hex: expr, $($remain:tt)* ) => {
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
assert_eq!(per_commitment_point.serialize()[..], hex::decode("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]);
- assert_eq!(chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &base_point).unwrap().serialize()[..],
+ assert_eq!(chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
hex::decode("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]);
- assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret).unwrap(),
+ assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret),
SecretKey::from_slice(&hex::decode("cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f").unwrap()[..]).unwrap());
- assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).unwrap().serialize()[..],
+ assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
hex::decode("02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0").unwrap()[..]);
- assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret).unwrap(),
+ assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret),
SecretKey::from_slice(&hex::decode("d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110").unwrap()[..]).unwrap());
}
use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RoutePath, RouteParameters};
use crate::ln::msgs;
use crate::ln::onion_utils;
+use crate::ln::onion_utils::HTLCFailReason;
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, MAX_VALUE_MSAT};
use crate::ln::wire::Encode;
use crate::chain::keysinterface::{Sign, KeysInterface, KeysManager, Recipient};
use crate::util::config::{UserConfig, ChannelConfig};
use crate::util::events::{Event, EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
-use crate::util::{byte_utils, events};
+use crate::util::events;
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
}
}
-#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
-pub(super) enum HTLCFailReason {
- LightningError {
- err: msgs::OnionErrorPacket,
- },
- Reason {
- failure_code: u16,
- data: Vec<u8>,
- }
-}
-
struct ReceiveError {
err_code: u16,
err_data: Vec<u8>,
msg: &'static str,
}
-/// Return value for claim_funds_from_hop
-enum ClaimFundsFromHop {
- PrevHopForceClosed,
- MonitorUpdateFail(PublicKey, MsgHandleErrInternal, Option<u64>),
- Success(u64),
- DuplicateClaim,
-}
-
type ShutdownResult = (Option<(OutPoint, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash, PublicKey, [u8; 32])>);
/// Error type returned across the channel_state mutex boundary. When an Err is generated for a
RevokeAndACKFirst,
}
+/// Information about a payment which is currently being claimed.
+struct ClaimingPayment {
+ amount_msat: u64,
+ payment_purpose: events::PaymentPurpose,
+ receiver_node_id: PublicKey,
+}
+impl_writeable_tlv_based!(ClaimingPayment, {
+ (0, amount_msat, required),
+ (2, payment_purpose, required),
+ (4, receiver_node_id, required),
+});
+
+/// Information about claimable or being-claimed payments
+struct ClaimablePayments {
+ /// Map from payment hash to the payment data and any HTLCs which are to us and can be
+ /// failed/claimed by the user.
+ ///
+ /// Note that, no consistency guarantees are made about the channels given here actually
+ /// existing anymore by the time you go to read them!
+ ///
+ /// When adding to the map, [`Self::pending_claiming_payments`] must also be checked to ensure
+ /// we don't get a duplicate payment.
+ claimable_htlcs: HashMap<PaymentHash, (events::PaymentPurpose, Vec<ClaimableHTLC>)>,
+
+ /// Map from payment hash to the payment data for HTLCs which we have begun claiming, but which
+ /// are waiting on a [`ChannelMonitorUpdate`] to complete in order to be surfaced to the user
+ /// as an [`events::Event::PaymentClaimed`].
+ pending_claiming_payments: HashMap<PaymentHash, ClaimingPayment>,
+}
+
// Note this is only exposed in cfg(test):
pub(super) struct ChannelHolder<Signer: Sign> {
pub(super) by_id: HashMap<[u8; 32], Channel<Signer>>,
ClosingMonitorUpdate((OutPoint, ChannelMonitorUpdate)),
}
+pub(crate) enum MonitorUpdateCompletionAction {
+ /// Indicates that a payment ultimately destined for us was claimed and we should emit an
+ /// [`events::Event::PaymentClaimed`] to the user if we haven't yet generated such an event for
+ /// this payment. Note that this is only best-effort. On restart it's possible such a duplicate
+ /// event can be generated.
+ PaymentClaimed { payment_hash: PaymentHash },
+ /// Indicates an [`events::Event`] should be surfaced to the user.
+ EmitEvent { event: events::Event },
+}
+
/// State we hold per-peer. In the future we should put channels in here, but for now we only hold
/// the latest Init features we heard from the peer.
struct PeerState {
// |
// |__`pending_inbound_payments`
// | |
-// | |__`claimable_htlcs`
+// | |__`claimable_payments`
// | |
// | |__`pending_outbound_payments`
// | |
channel_state: Mutex<ChannelHolder<<K::Target as KeysInterface>::Signer>>,
/// Storage for PaymentSecrets and any requirements on future inbound payments before we will
- /// expose them to users via a PaymentReceived event. HTLCs which do not meet the requirements
+ /// expose them to users via a PaymentClaimable event. HTLCs which do not meet the requirements
/// here are failed when we process them as pending-forwardable-HTLCs, and entries are removed
- /// after we generate a PaymentReceived upon receipt of all MPP parts or when they time out.
+ /// after we generate a PaymentClaimable upon receipt of all MPP parts or when they time out.
///
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_inbound_payments: Mutex<HashMap<PaymentHash, PendingInboundPayment>>,
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
- /// Map from payment hash to the payment data and any HTLCs which are to us and can be
- /// failed/claimed by the user.
- ///
- /// Note that, no consistency guarantees are made about the channels given here actually
- /// existing anymore by the time you go to read them!
+ /// The sets of payments which are claimable or currently being claimed. See
+ /// [`ClaimablePayments`]' individual field docs for more info.
///
/// See `ChannelManager` struct-level documentation for lock order requirements.
- claimable_htlcs: Mutex<HashMap<PaymentHash, (events::PaymentPurpose, Vec<ClaimableHTLC>)>>,
+ claimable_payments: Mutex<ClaimablePayments>,
/// The set of outbound SCID aliases across all our channels, including unconfirmed channels
/// and some closed channels which reached a usable state prior to being closed. This is used
pending_inbound_payments: Mutex::new(HashMap::new()),
pending_outbound_payments: Mutex::new(HashMap::new()),
forward_htlcs: Mutex::new(HashMap::new()),
- claimable_htlcs: Mutex::new(HashMap::new()),
+ claimable_payments: Mutex::new(ClaimablePayments { claimable_htlcs: HashMap::new(), pending_claiming_payments: HashMap::new() }),
pending_intercepted_htlcs: Mutex::new(HashMap::new()),
id_to_peer: Mutex::new(HashMap::new()),
short_to_chan_info: FairRwLock::new(HashMap::new()),
};
for htlc_source in failed_htlcs.drain(..) {
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: *channel_id };
- self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
}
let _ = handle_error!(self, result, *counterparty_node_id);
log_debug!(self.logger, "Finishing force-closure of channel with {} HTLCs to fail", failed_htlcs.len());
for htlc_source in failed_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
- self.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
if let Some((funding_txo, monitor_update)) = monitor_update_option {
// There isn't anything we can do if we get an update failure - we're already
return Err(ReceiveError {
msg: "Upstream node set CLTV to the wrong value",
err_code: 18,
- err_data: byte_utils::be32_to_array(cltv_expiry).to_vec()
+ err_data: cltv_expiry.to_be_bytes().to_vec()
})
}
// final_expiry_too_soon
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
- if (hop_data.outgoing_cltv_value as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
+ let current_height: u32 = self.best_block.read().unwrap().height();
+ if (hop_data.outgoing_cltv_value as u64) <= current_height as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
+ let mut err_data = Vec::with_capacity(12);
+ err_data.extend_from_slice(&amt_msat.to_be_bytes());
+ err_data.extend_from_slice(¤t_height.to_be_bytes());
return Err(ReceiveError {
- err_code: 17,
- err_data: Vec::new(),
+ err_code: 0x4000 | 15, err_data,
msg: "The final CLTV expiry is too soon to handle",
});
}
if hop_data.amt_to_forward > amt_msat {
return Err(ReceiveError {
err_code: 19,
- err_data: byte_utils::be64_to_array(amt_msat).to_vec(),
+ err_data: amt_msat.to_be_bytes().to_vec(),
msg: "Upstream node sent less than we were supposed to receive in payment",
});
}
return PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
channel_id: msg.channel_id,
htlc_id: msg.htlc_id,
- reason: onion_utils::build_first_hop_failure_packet(&shared_secret, $err_code, $data),
+ reason: HTLCFailReason::reason($err_code, $data.to_vec())
+ .get_encrypted_failure_packet(&shared_secret, &None),
}));
}
}
// with a short_channel_id of 0. This is important as various things later assume
// short_channel_id is non-0 in any ::Forward.
if let &PendingHTLCRouting::Forward { ref short_channel_id, .. } = routing {
- if let Some((err, code, chan_update)) = loop {
+ if let Some((err, mut code, chan_update)) = loop {
let id_option = self.short_to_chan_info.read().unwrap().get(&short_channel_id).cloned();
let mut channel_state = self.channel_state.lock().unwrap();
let forwarding_id_opt = match id_option {
}
chan_update_opt
} else {
- if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + MIN_CLTV_EXPIRY_DELTA as u64 { // incorrect_cltv_expiry
+ if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + MIN_CLTV_EXPIRY_DELTA as u64 {
+ // We really should set `incorrect_cltv_expiry` here but as we're not
+ // forwarding over a real channel we can't generate a channel_update
+ // for it. Instead we just return a generic temporary_node_failure.
break Some((
"Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta",
- 0x1000 | 13, None,
+ 0x2000 | 2, None,
));
}
None
(chan_update.serialized_length() as u16 + 2).write(&mut res).expect("Writes cannot fail");
msgs::ChannelUpdate::TYPE.write(&mut res).expect("Writes cannot fail");
chan_update.write(&mut res).expect("Writes cannot fail");
+ } else if code & 0x1000 == 0x1000 {
+ // If we're trying to return an error that requires a `channel_update` but
+ // we're forwarding to a phantom or intercept "channel" (i.e. cannot
+ // generate an update), just use the generic "temporary_node_failure"
+ // instead.
+ code = 0x2000 | 2;
}
return_err!(err, code, &res.0[..]);
}
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
- .map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"})?;
+ .map_err(|_| APIError::InvalidRoute{err: "Pubkey along hop was maliciously selected"})?;
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, payment_secret, cur_height, keysend_preimage)?;
if onion_utils::route_size_insane(&onion_payloads) {
- return Err(APIError::RouteError{err: "Route size too large considering onion data"});
+ return Err(APIError::InvalidRoute{err: "Route size too large considering onion data"});
}
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
match {
if chan.get().get_counterparty_node_id() != path.first().unwrap().pubkey {
- return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
+ return Err(APIError::InvalidRoute{err: "Node ID mismatch on first hop!"});
}
if !chan.get().is_live() {
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()});
/// fields for more info.
///
/// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
- /// method will error with an [`APIError::RouteError`]. Note, however, that once a payment
+ /// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
/// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
/// [`Event::PaymentSent`]) LDK will not stop you from sending a second payment with the same
/// [`PaymentId`].
/// PaymentSendFailure for more info.
///
/// In general, a path may raise:
- /// * [`APIError::RouteError`] when an invalid route or forwarding parameter (cltv_delta, fee,
+ /// * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
/// node public key) is specified.
/// * [`APIError::ChannelUnavailable`] if the next-hop channel is not available for updates
/// (including due to previous monitor update failure or new permanent monitor update
fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
if route.paths.len() < 1 {
- return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
+ return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over"}));
}
if payment_secret.is_none() && route.paths.len() > 1 {
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_string()}));
let mut path_errs = Vec::with_capacity(route.paths.len());
'path_check: for path in route.paths.iter() {
if path.len() < 1 || path.len() > 20 {
- path_errs.push(Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size"}));
continue 'path_check;
}
for (idx, hop) in path.iter().enumerate() {
if idx != path.len() - 1 && hop.pubkey == our_node_id {
- path_errs.push(Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us"}));
continue 'path_check;
}
}
/// Signals that no further retries for the given payment will occur.
///
- /// After this method returns, any future calls to [`retry_payment`] for the given `payment_id`
- /// will fail with [`PaymentSendFailure::ParameterError`]. If no such event has been generated,
- /// an [`Event::PaymentFailed`] event will be generated as soon as there are no remaining
- /// pending HTLCs for this payment.
+ /// After this method returns, no future calls to [`retry_payment`] for the given `payment_id`
+ /// are allowed. If no [`Event::PaymentFailed`] event had been generated before, one will be
+ /// generated as soon as there are no remaining pending HTLCs for this payment.
///
/// Note that calling this method does *not* prevent a payment from succeeding. You must still
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
/// determine the ultimate status of a payment.
///
+ /// If an [`Event::PaymentFailed`] event is generated and we restart without this
+ /// [`ChannelManager`] having been persisted, the payment may still be in the pending state
+ /// upon restart. This allows further calls to [`retry_payment`] (and requiring a second call
+ /// to [`abandon_payment`] to mark the payment as failed again). Otherwise, future calls to
+ /// [`retry_payment`] will fail with [`PaymentSendFailure::ParameterError`].
+ ///
+ /// [`abandon_payment`]: Self::abandon_payment
/// [`retry_payment`]: Self::retry_payment
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
/// [`Event::PaymentSent`]: events::Event::PaymentSent
let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
Some(chan) => {
if !chan.is_usable() {
- return Err(APIError::APIMisuseError {
- err: format!("Channel with id {:?} not fully established", next_hop_channel_id)
+ return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not fully established", log_bytes!(*next_hop_channel_id))
})
}
chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias())
},
- None => return Err(APIError::APIMisuseError {
- err: format!("Channel with id {:?} not found", next_hop_channel_id)
+ None => return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not found", log_bytes!(*next_hop_channel_id))
})
};
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with intercept id {:?} not found", intercept_id.0)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
let routing = match payment.forward_info.routing {
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with InterceptId {:?} not found", intercept_id)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
phantom_shared_secret: None,
});
- let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() };
+ let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
let destination = HTLCDestination::UnknownNextHop { requested_forward_scid: short_channel_id };
- self.fail_htlc_backwards_internal(htlc_source, &payment.forward_info.payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment.forward_info.payment_hash, &failure_reason, destination);
} else { unreachable!() } // Only `PendingHTLCRouting::Forward`s are intercepted
Ok(())
let mut new_events = Vec::new();
let mut failed_forwards = Vec::new();
let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
- let mut handle_errors = Vec::new();
{
let mut forward_htlcs = HashMap::new();
mem::swap(&mut forward_htlcs, &mut self.forward_htlcs.lock().unwrap());
};
failed_forwards.push((htlc_source, payment_hash,
- HTLCFailReason::Reason { failure_code: $err_code, data: $err_data },
+ HTLCFailReason::reason($err_code, $err_data),
reason
));
continue;
continue;
},
hash_map::Entry::Occupied(mut chan) => {
- let mut add_htlc_msgs = Vec::new();
- let mut fail_htlc_msgs = Vec::new();
for forward_info in pending_forwards.drain(..) {
match forward_info {
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
// Phantom payments are only PendingHTLCRouting::Receive.
phantom_shared_secret: None,
});
- match chan.get_mut().send_htlc(outgoing_amt_msat, payment_hash, outgoing_cltv_value, htlc_source.clone(), onion_packet, &self.logger) {
- Err(e) => {
- if let ChannelError::Ignore(msg) = e {
- log_trace!(self.logger, "Failed to forward HTLC with payment_hash {}: {}", log_bytes!(payment_hash.0), msg);
- } else {
- panic!("Stated return value requirements in send_htlc() were not met");
- }
- let (failure_code, data) = self.get_htlc_temp_fail_err_and_data(0x1000|7, short_chan_id, chan.get());
- failed_forwards.push((htlc_source, payment_hash,
- HTLCFailReason::Reason { failure_code, data },
- HTLCDestination::NextHopChannel { node_id: Some(chan.get().get_counterparty_node_id()), channel_id: forward_chan_id }
- ));
- continue;
- },
- Ok(update_add) => {
- match update_add {
- Some(msg) => { add_htlc_msgs.push(msg); },
- None => {
- // Nothing to do here...we're waiting on a remote
- // revoke_and_ack before we can add anymore HTLCs. The Channel
- // will automatically handle building the update_add_htlc and
- // commitment_signed messages when we can.
- // TODO: Do some kind of timer to set the channel as !is_live()
- // as we don't really want others relying on us relaying through
- // this channel currently :/.
- }
- }
+ if let Err(e) = chan.get_mut().queue_add_htlc(outgoing_amt_msat,
+ payment_hash, outgoing_cltv_value, htlc_source.clone(),
+ onion_packet, &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);
+ } else {
+ panic!("Stated return value requirements in send_htlc() were not met");
}
+ let (failure_code, data) = self.get_htlc_temp_fail_err_and_data(0x1000|7, short_chan_id, chan.get());
+ failed_forwards.push((htlc_source, payment_hash,
+ HTLCFailReason::reason(failure_code, data),
+ HTLCDestination::NextHopChannel { node_id: Some(chan.get().get_counterparty_node_id()), channel_id: forward_chan_id }
+ ));
+ continue;
}
},
HTLCForwardInfo::AddHTLC { .. } => {
},
HTLCForwardInfo::FailHTLC { htlc_id, err_packet } => {
log_trace!(self.logger, "Failing HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id);
- match chan.get_mut().get_update_fail_htlc(htlc_id, err_packet, &self.logger) {
- Err(e) => {
- if let ChannelError::Ignore(msg) = e {
- log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
- } else {
- panic!("Stated return value requirements in get_update_fail_htlc() were not met");
- }
- // fail-backs are best-effort, we probably already have one
- // pending, and if not that's OK, if not, the channel is on
- // the chain and sending the HTLC-Timeout is their problem.
- continue;
- },
- Ok(Some(msg)) => { fail_htlc_msgs.push(msg); },
- Ok(None) => {
- // Nothing to do here...we're waiting on a remote
- // revoke_and_ack before we can update the commitment
- // transaction. The Channel will automatically handle
- // building the update_fail_htlc and commitment_signed
- // messages when we can.
- // We don't need any kind of timer here as they should fail
- // the channel onto the chain if they can't get our
- // update_fail_htlc in time, it's not our problem.
+ if let Err(e) = chan.get_mut().queue_fail_htlc(
+ htlc_id, err_packet, &self.logger
+ ) {
+ if let ChannelError::Ignore(msg) = e {
+ log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
+ } else {
+ panic!("Stated return value requirements in queue_fail_htlc() were not met");
}
+ // fail-backs are best-effort, we probably already have one
+ // pending, and if not that's OK, if not, the channel is on
+ // the chain and sending the HTLC-Timeout is their problem.
+ continue;
}
},
}
}
-
- if !add_htlc_msgs.is_empty() || !fail_htlc_msgs.is_empty() {
- let (commitment_msg, monitor_update) = match chan.get_mut().send_commitment(&self.logger) {
- Ok(res) => res,
- Err(e) => {
- // We surely failed send_commitment due to bad keys, in that case
- // close channel and then send error message to peer.
- let counterparty_node_id = chan.get().get_counterparty_node_id();
- let err: Result<(), _> = match e {
- ChannelError::Ignore(_) | ChannelError::Warn(_) => {
- panic!("Stated return value requirements in send_commitment() were not met");
- }
- ChannelError::Close(msg) => {
- log_trace!(self.logger, "Closing channel {} due to Close-required error: {}", log_bytes!(chan.key()[..]), msg);
- let mut channel = remove_channel!(self, chan);
- // ChannelClosed event is generated by handle_error for us.
- Err(MsgHandleErrInternal::from_finish_shutdown(msg, channel.channel_id(), channel.get_user_id(), channel.force_shutdown(true), self.get_channel_update_for_broadcast(&channel).ok()))
- },
- };
- handle_errors.push((counterparty_node_id, err));
- continue;
- }
- };
- match self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
- ChannelMonitorUpdateStatus::Completed => {},
- e => {
- handle_errors.push((chan.get().get_counterparty_node_id(), handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, false, true)));
- continue;
- }
- }
- log_debug!(self.logger, "Forwarding HTLCs resulted in a commitment update with {} HTLCs added and {} HTLCs failed for channel {}",
- add_htlc_msgs.len(), fail_htlc_msgs.len(), log_bytes!(chan.get().channel_id()));
- channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
- node_id: chan.get().get_counterparty_node_id(),
- updates: msgs::CommitmentUpdate {
- update_add_htlcs: add_htlc_msgs,
- update_fulfill_htlcs: Vec::new(),
- update_fail_htlcs: fail_htlc_msgs,
- update_fail_malformed_htlcs: Vec::new(),
- update_fee: None,
- commitment_signed: commitment_msg,
- },
- });
- }
}
}
} else {
macro_rules! fail_htlc {
($htlc: expr, $payment_hash: expr) => {
- let mut htlc_msat_height_data = byte_utils::be64_to_array($htlc.value).to_vec();
+ let mut htlc_msat_height_data = $htlc.value.to_be_bytes().to_vec();
htlc_msat_height_data.extend_from_slice(
- &byte_utils::be32_to_array(self.best_block.read().unwrap().height()),
+ &self.best_block.read().unwrap().height().to_be_bytes(),
);
failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
short_channel_id: $htlc.prev_hop.short_channel_id,
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
phantom_shared_secret,
}), payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
+ HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
HTLCDestination::FailedPayment { payment_hash: $payment_hash },
));
}
macro_rules! check_total_value {
($payment_data: expr, $payment_preimage: expr) => {{
- let mut payment_received_generated = false;
+ let mut payment_claimable_generated = false;
let purpose = || {
events::PaymentPurpose::InvoicePayment {
payment_preimage: $payment_preimage,
payment_secret: $payment_data.payment_secret,
}
};
- let mut claimable_htlcs = self.claimable_htlcs.lock().unwrap();
- let (_, htlcs) = claimable_htlcs.entry(payment_hash)
+ let mut claimable_payments = self.claimable_payments.lock().unwrap();
+ if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
+ fail_htlc!(claimable_htlc, payment_hash);
+ continue
+ }
+ let (_, htlcs) = claimable_payments.claimable_htlcs.entry(payment_hash)
.or_insert_with(|| (purpose(), Vec::new()));
if htlcs.len() == 1 {
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
} else if total_value == $payment_data.total_msat {
let prev_channel_id = prev_funding_outpoint.to_channel_id();
htlcs.push(claimable_htlc);
- new_events.push(events::Event::PaymentReceived {
+ new_events.push(events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
purpose: purpose(),
via_channel_id: Some(prev_channel_id),
via_user_channel_id: Some(prev_user_channel_id),
});
- payment_received_generated = true;
+ payment_claimable_generated = true;
} else {
// Nothing to do - we haven't reached the total
// payment value yet, wait until we receive more
// MPP parts.
htlcs.push(claimable_htlc);
}
- payment_received_generated
+ payment_claimable_generated
}}
}
check_total_value!(payment_data, payment_preimage);
},
OnionPayload::Spontaneous(preimage) => {
- match self.claimable_htlcs.lock().unwrap().entry(payment_hash) {
+ let mut claimable_payments = self.claimable_payments.lock().unwrap();
+ if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
+ fail_htlc!(claimable_htlc, payment_hash);
+ continue
+ }
+ match claimable_payments.claimable_htlcs.entry(payment_hash) {
hash_map::Entry::Vacant(e) => {
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
e.insert((purpose.clone(), vec![claimable_htlc]));
let prev_channel_id = prev_funding_outpoint.to_channel_id();
- new_events.push(events::Event::PaymentReceived {
+ new_events.push(events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
amount_msat: outgoing_amt_msat,
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
fail_htlc!(claimable_htlc, payment_hash);
} else {
- let payment_received_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
- if payment_received_generated {
+ let payment_claimable_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
+ if payment_claimable_generated {
inbound_payment.remove_entry();
}
}
}
for (htlc_source, payment_hash, failure_reason, destination) in failed_forwards.drain(..) {
- self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
}
self.forward_htlcs(&mut phantom_receives);
- for (counterparty_node_id, err) in handle_errors.drain(..) {
- let _ = handle_error!(self, err, counterparty_node_id);
- }
+ // Freeing the holding cell here is relatively redundant - in practice we'll do it when we
+ // next get a `get_and_clear_pending_msg_events` call, but some tests rely on it, and it's
+ // nice to do the work now if we can rather than while we're trying to get messages in the
+ // network stack.
+ self.check_free_holding_cells();
if new_events.is_empty() { return }
let mut events = self.pending_events.lock().unwrap();
self.process_background_events();
}
- fn update_channel_fee(&self, pending_msg_events: &mut Vec<events::MessageSendEvent>, chan_id: &[u8; 32], chan: &mut Channel<<K::Target as KeysInterface>::Signer>, new_feerate: u32) -> (bool, NotifyOption, Result<(), MsgHandleErrInternal>) {
- if !chan.is_outbound() { return (true, NotifyOption::SkipPersist, Ok(())); }
+ fn update_channel_fee(&self, chan_id: &[u8; 32], chan: &mut Channel<<K::Target as KeysInterface>::Signer>, new_feerate: u32) -> NotifyOption {
+ if !chan.is_outbound() { return NotifyOption::SkipPersist; }
// If the feerate has decreased by less than half, don't bother
if new_feerate <= chan.get_feerate() && new_feerate * 2 > chan.get_feerate() {
log_trace!(self.logger, "Channel {} does not qualify for a feerate change from {} to {}.",
log_bytes!(chan_id[..]), chan.get_feerate(), new_feerate);
- return (true, NotifyOption::SkipPersist, Ok(()));
+ return NotifyOption::SkipPersist;
}
if !chan.is_live() {
log_trace!(self.logger, "Channel {} does not qualify for a feerate change from {} to {} as it cannot currently be updated (probably the peer is disconnected).",
log_bytes!(chan_id[..]), chan.get_feerate(), new_feerate);
- return (true, NotifyOption::SkipPersist, Ok(()));
+ return NotifyOption::SkipPersist;
}
log_trace!(self.logger, "Channel {} qualifies for a feerate change from {} to {}.",
log_bytes!(chan_id[..]), chan.get_feerate(), new_feerate);
- let mut retain_channel = true;
- let res = match chan.send_update_fee_and_commit(new_feerate, &self.logger) {
- Ok(res) => Ok(res),
- Err(e) => {
- let (drop, res) = convert_chan_err!(self, e, chan, chan_id);
- if drop { retain_channel = false; }
- Err(res)
- }
- };
- let ret_err = match res {
- Ok(Some((update_fee, commitment_signed, monitor_update))) => {
- match self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
- ChannelMonitorUpdateStatus::Completed => {
- pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
- node_id: chan.get_counterparty_node_id(),
- updates: msgs::CommitmentUpdate {
- update_add_htlcs: Vec::new(),
- update_fulfill_htlcs: Vec::new(),
- update_fail_htlcs: Vec::new(),
- update_fail_malformed_htlcs: Vec::new(),
- update_fee: Some(update_fee),
- commitment_signed,
- },
- });
- Ok(())
- },
- e => {
- let (res, drop) = handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, chan_id, COMMITMENT_UPDATE_ONLY);
- if drop { retain_channel = false; }
- res
- }
- }
- },
- Ok(None) => Ok(()),
- Err(e) => Err(e),
- };
- (retain_channel, NotifyOption::DoPersist, ret_err)
+ chan.queue_update_fee(new_feerate, &self.logger);
+ NotifyOption::DoPersist
}
#[cfg(fuzzing)]
let new_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
- let mut handle_errors = Vec::new();
- {
- let mut channel_state_lock = self.channel_state.lock().unwrap();
- let channel_state = &mut *channel_state_lock;
- let pending_msg_events = &mut channel_state.pending_msg_events;
- channel_state.by_id.retain(|chan_id, chan| {
- let (retain_channel, chan_needs_persist, err) = self.update_channel_fee(pending_msg_events, chan_id, chan, new_feerate);
- if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
- if err.is_err() {
- handle_errors.push(err);
- }
- retain_channel
- });
+ let mut channel_state = self.channel_state.lock().unwrap();
+ for (chan_id, chan) in channel_state.by_id.iter_mut() {
+ let chan_needs_persist = self.update_channel_fee(chan_id, chan, new_feerate);
+ if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
}
should_persist
let new_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
- let mut handle_errors = Vec::new();
+ let mut handle_errors: Vec<(Result<(), _>, _)> = Vec::new();
let mut timed_out_mpp_htlcs = Vec::new();
{
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
let pending_msg_events = &mut channel_state.pending_msg_events;
channel_state.by_id.retain(|chan_id, chan| {
- let counterparty_node_id = chan.get_counterparty_node_id();
- let (retain_channel, chan_needs_persist, err) = self.update_channel_fee(pending_msg_events, chan_id, chan, new_feerate);
+ let chan_needs_persist = self.update_channel_fee(chan_id, chan, new_feerate);
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
- if err.is_err() {
- handle_errors.push((err, counterparty_node_id));
- }
- if !retain_channel { return false; }
if let Err(e) = chan.timer_check_closing_negotiation_progress() {
let (needs_close, err) = convert_chan_err!(self, e, chan, chan_id);
});
}
- self.claimable_htlcs.lock().unwrap().retain(|payment_hash, (_, htlcs)| {
+ self.claimable_payments.lock().unwrap().claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
if htlcs.is_empty() {
// This should be unreachable
debug_assert!(false);
});
for htlc_source in timed_out_mpp_htlcs.drain(..) {
+ let source = HTLCSource::PreviousHopData(htlc_source.0.clone());
+ let reason = HTLCFailReason::from_failure_code(23);
let receiver = HTLCDestination::FailedPayment { payment_hash: htlc_source.1 };
- self.fail_htlc_backwards_internal(HTLCSource::PreviousHopData(htlc_source.0.clone()), &htlc_source.1, HTLCFailReason::Reason { failure_code: 23, data: Vec::new() }, receiver );
+ self.fail_htlc_backwards_internal(&source, &htlc_source.1, &reason, receiver);
}
for (err, counterparty_node_id) in handle_errors.drain(..) {
self.remove_stale_resolved_payments();
+ // Technically we don't need to do this here, but if we have holding cell entries in a
+ // channel that need freeing, it's better to do that here and block a background task
+ // than block the message queueing pipeline.
+ if self.check_free_holding_cells() {
+ should_persist = NotifyOption::DoPersist;
+ }
+
should_persist
});
}
/// Indicates that the preimage for payment_hash is unknown or the received amount is incorrect
- /// after a PaymentReceived event, failing the HTLC back to its origin and freeing resources
+ /// after a PaymentClaimable event, failing the HTLC back to its origin and freeing resources
/// along the path (including in our own channel on which we received it).
///
/// Note that in some cases around unclean shutdown, it is possible the payment may have
/// already been claimed by you via [`ChannelManager::claim_funds`] prior to you seeing (a
- /// second copy of) the [`events::Event::PaymentReceived`] event. Alternatively, the payment
+ /// second copy of) the [`events::Event::PaymentClaimable`] event. Alternatively, the payment
/// may have already been failed automatically by LDK if it was nearing its expiration time.
///
/// While LDK will never claim a payment automatically on your behalf (i.e. without you calling
pub fn fail_htlc_backwards(&self, payment_hash: &PaymentHash) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- let removed_source = self.claimable_htlcs.lock().unwrap().remove(payment_hash);
+ let removed_source = self.claimable_payments.lock().unwrap().claimable_htlcs.remove(payment_hash);
if let Some((_, mut sources)) = removed_source {
for htlc in sources.drain(..) {
- let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
- htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
- self.best_block.read().unwrap().height()));
- self.fail_htlc_backwards_internal(
- HTLCSource::PreviousHopData(htlc.prev_hop), payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
- HTLCDestination::FailedPayment { payment_hash: *payment_hash });
+ let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
+ htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
+ let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+ let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+ let receiver = HTLCDestination::FailedPayment { payment_hash: *payment_hash };
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
}
}
&self, mut htlcs_to_fail: Vec<(HTLCSource, PaymentHash)>, channel_id: [u8; 32],
counterparty_node_id: &PublicKey
) {
- for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
- let (failure_code, onion_failure_data) =
- match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
- hash_map::Entry::Occupied(chan_entry) => {
- self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
- },
- hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
- };
+ let (failure_code, onion_failure_data) =
+ match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
+ hash_map::Entry::Occupied(chan_entry) => {
+ self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
+ },
+ hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
+ };
+ for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
+ let reason = HTLCFailReason::reason(failure_code, onion_failure_data.clone());
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
- self.fail_htlc_backwards_internal(htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data }, receiver);
+ self.fail_htlc_backwards_internal(&htlc_src, &payment_hash, &reason, receiver);
}
}
/// Fails an HTLC backwards to the sender of it to us.
/// Note that we do not assume that channels corresponding to failed HTLCs are still available.
- fn fail_htlc_backwards_internal(&self, source: HTLCSource, payment_hash: &PaymentHash, onion_error: HTLCFailReason,destination: HTLCDestination) {
+ fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
#[cfg(debug_assertions)]
{
// Ensure that the `channel_state` lock is not held when calling this function.
// from block_connected which may run during initialization prior to the chain_monitor
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
- HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payment_params, .. } => {
+ HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, ref payment_params, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let mut all_paths_failed = false;
let mut full_failure_ev = None;
- if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+ if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
return;
all_paths_failed = true;
if payment.get().abandoned() {
full_failure_ev = Some(events::Event::PaymentFailed {
- payment_id,
+ payment_id: *payment_id,
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
});
payment.remove();
} else { None };
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
- let path_failure = match &onion_error {
- &HTLCFailReason::LightningError { ref err } => {
+ let path_failure = {
#[cfg(test)]
- let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+ let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(&self.secp_ctx, &self.logger, &source);
#[cfg(not(test))]
- let (network_update, short_channel_id, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+ let (network_update, short_channel_id, payment_retryable, _, _) = onion_error.decode_onion_failure(&self.secp_ctx, &self.logger, &source);
- if self.payment_is_probe(payment_hash, &payment_id) {
- if !payment_retryable {
- events::Event::ProbeSuccessful {
- payment_id,
- payment_hash: payment_hash.clone(),
- path: path.clone(),
- }
- } else {
- events::Event::ProbeFailed {
- payment_id,
- payment_hash: payment_hash.clone(),
- path: path.clone(),
- short_channel_id,
- }
- }
- } else {
- // TODO: If we decided to blame ourselves (or one of our channels) in
- // process_onion_failure we should close that channel as it implies our
- // next-hop is needlessly blaming us!
- if let Some(scid) = short_channel_id {
- retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
- }
- events::Event::PaymentPathFailed {
- payment_id: Some(payment_id),
+ if self.payment_is_probe(payment_hash, &payment_id) {
+ if !payment_retryable {
+ events::Event::ProbeSuccessful {
+ payment_id: *payment_id,
payment_hash: payment_hash.clone(),
- payment_failed_permanently: !payment_retryable,
- network_update,
- all_paths_failed,
path: path.clone(),
- short_channel_id,
- retry,
- #[cfg(test)]
- error_code: onion_error_code,
- #[cfg(test)]
- error_data: onion_error_data
- }
- }
- },
- &HTLCFailReason::Reason {
-#[cfg(test)]
- ref failure_code,
-#[cfg(test)]
- ref data,
- .. } => {
- // we get a fail_malformed_htlc from the first hop
- // TODO: We'd like to generate a NetworkUpdate for temporary
- // failures here, but that would be insufficient as find_route
- // generally ignores its view of our own channels as we provide them via
- // ChannelDetails.
- // TODO: For non-temporary failures, we really should be closing the
- // channel here as we apparently can't relay through them anyway.
- let scid = path.first().unwrap().short_channel_id;
- retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
-
- if self.payment_is_probe(payment_hash, &payment_id) {
- events::Event::ProbeFailed {
- payment_id,
- payment_hash: payment_hash.clone(),
- path: path.clone(),
- short_channel_id: Some(scid),
}
} else {
- events::Event::PaymentPathFailed {
- payment_id: Some(payment_id),
+ events::Event::ProbeFailed {
+ payment_id: *payment_id,
payment_hash: payment_hash.clone(),
- payment_failed_permanently: false,
- network_update: None,
- all_paths_failed,
path: path.clone(),
- short_channel_id: Some(scid),
- retry,
-#[cfg(test)]
- error_code: Some(*failure_code),
-#[cfg(test)]
- error_data: Some(data.clone()),
+ short_channel_id,
}
}
+ } else {
+ // TODO: If we decided to blame ourselves (or one of our channels) in
+ // process_onion_failure we should close that channel as it implies our
+ // next-hop is needlessly blaming us!
+ if let Some(scid) = short_channel_id {
+ retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
+ }
+ events::Event::PaymentPathFailed {
+ payment_id: Some(*payment_id),
+ payment_hash: payment_hash.clone(),
+ payment_failed_permanently: !payment_retryable,
+ network_update,
+ all_paths_failed,
+ path: path.clone(),
+ short_channel_id,
+ retry,
+ #[cfg(test)]
+ error_code: onion_error_code,
+ #[cfg(test)]
+ error_data: onion_error_data
+ }
}
};
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push(path_failure);
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
},
- HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, phantom_shared_secret, outpoint }) => {
- let err_packet = match onion_error {
- HTLCFailReason::Reason { failure_code, data } => {
- log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with code {}", log_bytes!(payment_hash.0), failure_code);
- if let Some(phantom_ss) = phantom_shared_secret {
- let phantom_packet = onion_utils::build_failure_packet(&phantom_ss, failure_code, &data[..]).encode();
- let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(&phantom_ss, &phantom_packet);
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
- } else {
- let packet = onion_utils::build_failure_packet(&incoming_packet_shared_secret, failure_code, &data[..]).encode();
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &packet)
- }
- },
- HTLCFailReason::LightningError { err } => {
- log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards with pre-built LightningError", log_bytes!(payment_hash.0));
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &err.data)
- }
- };
+ 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);
+ let err_packet = onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret);
let mut forward_event = None;
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
if forward_htlcs.is_empty() {
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
}
- match forward_htlcs.entry(short_channel_id) {
+ match forward_htlcs.entry(*short_channel_id) {
hash_map::Entry::Occupied(mut entry) => {
- entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id, err_packet });
+ entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet });
},
hash_map::Entry::Vacant(entry) => {
- entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id, err_packet }));
+ entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }));
}
}
mem::drop(forward_htlcs);
}
pending_events.push(events::Event::HTLCHandlingFailed {
prev_channel_id: outpoint.to_channel_id(),
- failed_next_destination: destination
+ failed_next_destination: destination,
});
},
}
}
- /// Provides a payment preimage in response to [`Event::PaymentReceived`], generating any
+ /// Provides a payment preimage in response to [`Event::PaymentClaimable`], generating any
/// [`MessageSendEvent`]s needed to claim the payment.
///
/// Note that calling this method does *not* guarantee that the payment has been claimed. You
/// provided to your [`EventHandler`] when [`process_pending_events`] is next called.
///
/// Note that if you did not set an `amount_msat` when calling [`create_inbound_payment`] or
- /// [`create_inbound_payment_for_hash`] you must check that the amount in the `PaymentReceived`
+ /// [`create_inbound_payment_for_hash`] you must check that the amount in the `PaymentClaimable`
/// event matches your expectation. If you fail to do so and call this method, you may provide
/// the sender "proof-of-payment" when they did not fulfill the full expected payment.
///
- /// [`Event::PaymentReceived`]: crate::util::events::Event::PaymentReceived
+ /// [`Event::PaymentClaimable`]: crate::util::events::Event::PaymentClaimable
/// [`Event::PaymentClaimed`]: crate::util::events::Event::PaymentClaimed
/// [`process_pending_events`]: EventsProvider::process_pending_events
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
- /// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events
pub fn claim_funds(&self, payment_preimage: PaymentPreimage) {
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- let removed_source = self.claimable_htlcs.lock().unwrap().remove(&payment_hash);
- if let Some((payment_purpose, mut sources)) = removed_source {
- assert!(!sources.is_empty());
-
- // If we are claiming an MPP payment, we have to take special care to ensure that each
- // channel exists before claiming all of the payments (inside one lock).
- // Note that channel existance is sufficient as we should always get a monitor update
- // which will take care of the real HTLC claim enforcement.
- //
- // If we find an HTLC which we would need to claim but for which we do not have a
- // channel, we will fail all parts of the MPP payment. While we could wait and see if
- // the sender retries the already-failed path(s), it should be a pretty rare case where
- // we got all the HTLCs and then a channel closed while we were waiting for the user to
- // provide the preimage, so worrying too much about the optimal handling isn't worth
- // it.
- let mut claimable_amt_msat = 0;
- let mut expected_amt_msat = None;
- let mut valid_mpp = true;
- let mut errs = Vec::new();
- let mut claimed_any_htlcs = false;
- let mut channel_state_lock = self.channel_state.lock().unwrap();
- let channel_state = &mut *channel_state_lock;
- let mut receiver_node_id = Some(self.our_network_pubkey);
- for htlc in sources.iter() {
- let chan_id = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
- Some((_cp_id, chan_id)) => chan_id.clone(),
- None => {
- valid_mpp = false;
+ let mut sources = {
+ let mut claimable_payments = self.claimable_payments.lock().unwrap();
+ if let Some((payment_purpose, sources)) = claimable_payments.claimable_htlcs.remove(&payment_hash) {
+ let mut receiver_node_id = self.our_network_pubkey;
+ for htlc in sources.iter() {
+ if htlc.prev_hop.phantom_shared_secret.is_some() {
+ let phantom_pubkey = self.keys_manager.get_node_id(Recipient::PhantomNode)
+ .expect("Failed to get node_id for phantom node recipient");
+ receiver_node_id = phantom_pubkey;
break;
}
- };
+ }
- if let None = channel_state.by_id.get(&chan_id) {
- valid_mpp = false;
- break;
+ let dup_purpose = claimable_payments.pending_claiming_payments.insert(payment_hash,
+ ClaimingPayment { amount_msat: sources.iter().map(|source| source.value).sum(),
+ payment_purpose, receiver_node_id,
+ });
+ 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));
}
+ sources
+ } else { return; }
+ };
+ debug_assert!(!sources.is_empty());
- if expected_amt_msat.is_some() && expected_amt_msat != Some(htlc.total_msat) {
- log_error!(self.logger, "Somehow ended up with an MPP payment with different total amounts - this should not be reachable!");
- debug_assert!(false);
+ // If we are claiming an MPP payment, we check that all channels which contain a claimable
+ // HTLC still exist. While this isn't guaranteed to remain true if a channel closes while
+ // we're claiming (or even after we claim, before the commitment update dance completes),
+ // it should be a relatively rare race, and we'd rather not claim HTLCs that require us to
+ // go on-chain (and lose the on-chain fee to do so) than just reject the payment.
+ //
+ // Note that we'll still always get our funds - as long as the generated
+ // `ChannelMonitorUpdate` makes it out to the relevant monitor we can claim on-chain.
+ //
+ // If we find an HTLC which we would need to claim but for which we do not have a
+ // channel, we will fail all parts of the MPP payment. While we could wait and see if
+ // the sender retries the already-failed path(s), it should be a pretty rare case where
+ // we got all the HTLCs and then a channel closed while we were waiting for the user to
+ // provide the preimage, so worrying too much about the optimal handling isn't worth
+ // it.
+ let mut claimable_amt_msat = 0;
+ let mut expected_amt_msat = None;
+ let mut valid_mpp = true;
+ let mut errs = Vec::new();
+ let mut channel_state = Some(self.channel_state.lock().unwrap());
+ for htlc in sources.iter() {
+ let chan_id = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
+ Some((_cp_id, chan_id)) => chan_id.clone(),
+ None => {
valid_mpp = false;
break;
}
- expected_amt_msat = Some(htlc.total_msat);
- if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
- // We don't currently support MPP for spontaneous payments, so just check
- // that there's one payment here and move on.
- if sources.len() != 1 {
- log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
- debug_assert!(false);
- valid_mpp = false;
- break;
- }
- }
- let phantom_shared_secret = htlc.prev_hop.phantom_shared_secret;
- if phantom_shared_secret.is_some() {
- let phantom_pubkey = self.keys_manager.get_node_id(Recipient::PhantomNode)
- .expect("Failed to get node_id for phantom node recipient");
- receiver_node_id = Some(phantom_pubkey)
- }
+ };
- claimable_amt_msat += htlc.value;
- }
- if sources.is_empty() || expected_amt_msat.is_none() {
- log_info!(self.logger, "Attempted to claim an incomplete payment which no longer had any available HTLCs!");
- return;
+ if let None = channel_state.as_ref().unwrap().by_id.get(&chan_id) {
+ valid_mpp = false;
+ break;
}
- if claimable_amt_msat != expected_amt_msat.unwrap() {
- log_info!(self.logger, "Attempted to claim an incomplete payment, expected {} msat, had {} available to claim.",
- expected_amt_msat.unwrap(), claimable_amt_msat);
- return;
+
+ if expected_amt_msat.is_some() && expected_amt_msat != Some(htlc.total_msat) {
+ log_error!(self.logger, "Somehow ended up with an MPP payment with different total amounts - this should not be reachable!");
+ debug_assert!(false);
+ valid_mpp = false;
+ break;
}
- if valid_mpp {
- for htlc in sources.drain(..) {
- match self.claim_funds_from_hop(&mut channel_state_lock, htlc.prev_hop, payment_preimage) {
- ClaimFundsFromHop::MonitorUpdateFail(pk, err, _) => {
- if let msgs::ErrorAction::IgnoreError = err.err.action {
- // We got a temporary failure updating monitor, but will claim the
- // HTLC when the monitor updating is restored (or on chain).
- log_error!(self.logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
- claimed_any_htlcs = true;
- } else { errs.push((pk, err)); }
- },
- ClaimFundsFromHop::PrevHopForceClosed => unreachable!("We already checked for channel existence, we can't fail here!"),
- ClaimFundsFromHop::DuplicateClaim => {
- // While we should never get here in most cases, if we do, it likely
- // indicates that the HTLC was timed out some time ago and is no longer
- // available to be claimed. Thus, it does not make sense to set
- // `claimed_any_htlcs`.
- },
- ClaimFundsFromHop::Success(_) => claimed_any_htlcs = true,
- }
+ expected_amt_msat = Some(htlc.total_msat);
+ if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
+ // We don't currently support MPP for spontaneous payments, so just check
+ // that there's one payment here and move on.
+ if sources.len() != 1 {
+ log_error!(self.logger, "Somehow ended up with an MPP spontaneous payment - this should not be reachable!");
+ debug_assert!(false);
+ valid_mpp = false;
+ break;
}
}
- mem::drop(channel_state_lock);
- if !valid_mpp {
- for htlc in sources.drain(..) {
- let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
- htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
- self.best_block.read().unwrap().height()));
- self.fail_htlc_backwards_internal(
- HTLCSource::PreviousHopData(htlc.prev_hop), &payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_height_data },
- HTLCDestination::FailedPayment { payment_hash } );
+
+ claimable_amt_msat += htlc.value;
+ }
+ if sources.is_empty() || expected_amt_msat.is_none() {
+ mem::drop(channel_state);
+ self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+ log_info!(self.logger, "Attempted to claim an incomplete payment which no longer had any available HTLCs!");
+ return;
+ }
+ if claimable_amt_msat != expected_amt_msat.unwrap() {
+ mem::drop(channel_state);
+ self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+ log_info!(self.logger, "Attempted to claim an incomplete payment, expected {} msat, had {} available to claim.",
+ expected_amt_msat.unwrap(), claimable_amt_msat);
+ return;
+ }
+ if valid_mpp {
+ for htlc in sources.drain(..) {
+ if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
+ if let Err((pk, err)) = self.claim_funds_from_hop(channel_state.take().unwrap(), htlc.prev_hop,
+ payment_preimage,
+ |_| Some(MonitorUpdateCompletionAction::PaymentClaimed { payment_hash }))
+ {
+ if let msgs::ErrorAction::IgnoreError = err.err.action {
+ // We got a temporary failure updating monitor, but will claim the
+ // HTLC when the monitor updating is restored (or on chain).
+ log_error!(self.logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
+ } else { errs.push((pk, err)); }
}
}
-
- if claimed_any_htlcs {
- self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
- receiver_node_id,
- payment_hash,
- purpose: payment_purpose,
- amount_msat: claimable_amt_msat,
- });
+ }
+ mem::drop(channel_state);
+ if !valid_mpp {
+ for htlc in sources.drain(..) {
+ let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
+ htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
+ let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+ let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+ let receiver = HTLCDestination::FailedPayment { payment_hash };
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
+ self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+ }
- // Now we can handle any errors which were generated.
- for (counterparty_node_id, err) in errs.drain(..) {
- let res: Result<(), _> = Err(err);
- let _ = handle_error!(self, res, counterparty_node_id);
- }
+ // Now we can handle any errors which were generated.
+ for (counterparty_node_id, err) in errs.drain(..) {
+ let res: Result<(), _> = Err(err);
+ let _ = handle_error!(self, res, counterparty_node_id);
}
}
- fn claim_funds_from_hop(&self, channel_state_lock: &mut MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage) -> ClaimFundsFromHop {
+ fn claim_funds_from_hop<ComplFunc: FnOnce(Option<u64>) -> Option<MonitorUpdateCompletionAction>>(&self,
+ mut channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>,
+ prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage, completion_action: ComplFunc)
+ -> Result<(), (PublicKey, MsgHandleErrInternal)> {
//TODO: Delay the claimed_funds relaying just like we do outbound relay!
let chan_id = prev_hop.outpoint.to_channel_id();
- let channel_state = &mut **channel_state_lock;
+ let channel_state = &mut *channel_state_lock;
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(chan_id) {
+ let counterparty_node_id = chan.get().get_counterparty_node_id();
match chan.get_mut().get_update_fulfill_htlc_and_commit(prev_hop.htlc_id, payment_preimage, &self.logger) {
Ok(msgs_monitor_option) => {
if let UpdateFulfillCommitFetch::NewClaim { msgs, htlc_value_msat, monitor_update } = msgs_monitor_option {
log_given_level!(self.logger, if e == ChannelMonitorUpdateStatus::PermanentFailure { Level::Error } else { Level::Debug },
"Failed to update channel monitor with preimage {:?}: {:?}",
payment_preimage, e);
- return ClaimFundsFromHop::MonitorUpdateFail(
- chan.get().get_counterparty_node_id(),
- handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, false, msgs.is_some()).unwrap_err(),
- Some(htlc_value_msat)
- );
+ let err = handle_monitor_update_res!(self, e, chan, RAACommitmentOrder::CommitmentFirst, false, msgs.is_some()).unwrap_err();
+ mem::drop(channel_state_lock);
+ self.handle_monitor_update_completion_actions(completion_action(Some(htlc_value_msat)));
+ return Err((counterparty_node_id, err));
}
}
if let Some((msg, commitment_signed)) = msgs {
}
});
}
- return ClaimFundsFromHop::Success(htlc_value_msat);
+ mem::drop(channel_state_lock);
+ self.handle_monitor_update_completion_actions(completion_action(Some(htlc_value_msat)));
+ Ok(())
} else {
- return ClaimFundsFromHop::DuplicateClaim;
+ Ok(())
}
},
Err((e, monitor_update)) => {
match self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
ChannelMonitorUpdateStatus::Completed => {},
e => {
+ // TODO: This needs to be handled somehow - if we receive a monitor update
+ // with a preimage we *must* somehow manage to propagate it to the upstream
+ // channel, or we must have an ability to receive the same update and try
+ // again on restart.
log_given_level!(self.logger, if e == ChannelMonitorUpdateStatus::PermanentFailure { Level::Error } else { Level::Info },
"Failed to update channel monitor with preimage {:?} immediately prior to force-close: {:?}",
payment_preimage, e);
},
}
- let counterparty_node_id = chan.get().get_counterparty_node_id();
let (drop, res) = convert_chan_err!(self, e, chan.get_mut(), &chan_id);
if drop {
chan.remove_entry();
}
- return ClaimFundsFromHop::MonitorUpdateFail(counterparty_node_id, res, None);
+ mem::drop(channel_state_lock);
+ self.handle_monitor_update_completion_actions(completion_action(None));
+ Err((counterparty_node_id, res))
},
}
- } else { return ClaimFundsFromHop::PrevHopForceClosed }
+ } else {
+ let preimage_update = ChannelMonitorUpdate {
+ update_id: CLOSED_CHANNEL_UPDATE_ID,
+ updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
+ payment_preimage,
+ }],
+ };
+ // We update the ChannelMonitor on the backward link, after
+ // receiving an `update_fulfill_htlc` from the forward link.
+ let update_res = self.chain_monitor.update_channel(prev_hop.outpoint, preimage_update);
+ if update_res != ChannelMonitorUpdateStatus::Completed {
+ // TODO: This needs to be handled somehow - if we receive a monitor update
+ // with a preimage we *must* somehow manage to propagate it to the upstream
+ // channel, or we must have an ability to receive the same event and try
+ // again on restart.
+ log_error!(self.logger, "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
+ payment_preimage, update_res);
+ }
+ mem::drop(channel_state_lock);
+ // Note that we do process the completion action here. This totally could be a
+ // duplicate claim, but we have no way of knowing without interrogating the
+ // `ChannelMonitor` we've provided the above update to. Instead, note that `Event`s are
+ // generally always allowed to be duplicative (and it's specifically noted in
+ // `PaymentForwarded`).
+ self.handle_monitor_update_completion_actions(completion_action(None));
+ Ok(())
+ }
}
fn finalize_claims(&self, mut sources: Vec<HTLCSource>) {
}
}
- fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, next_channel_id: [u8; 32]) {
+ fn claim_funds_internal(&self, channel_state_lock: MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, next_channel_id: [u8; 32]) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
mem::drop(channel_state_lock);
},
HTLCSource::PreviousHopData(hop_data) => {
let prev_outpoint = hop_data.outpoint;
- let res = self.claim_funds_from_hop(&mut channel_state_lock, hop_data, payment_preimage);
- let claimed_htlc = if let ClaimFundsFromHop::DuplicateClaim = res { false } else { true };
- let htlc_claim_value_msat = match res {
- ClaimFundsFromHop::MonitorUpdateFail(_, _, amt_opt) => amt_opt,
- ClaimFundsFromHop::Success(amt) => Some(amt),
- _ => None,
- };
- if let ClaimFundsFromHop::PrevHopForceClosed = res {
- let preimage_update = ChannelMonitorUpdate {
- update_id: CLOSED_CHANNEL_UPDATE_ID,
- updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
- payment_preimage: payment_preimage.clone(),
- }],
- };
- // We update the ChannelMonitor on the backward link, after
- // receiving an offchain preimage event from the forward link (the
- // event being update_fulfill_htlc).
- let update_res = self.chain_monitor.update_channel(prev_outpoint, preimage_update);
- if update_res != ChannelMonitorUpdateStatus::Completed {
- // TODO: This needs to be handled somehow - if we receive a monitor update
- // with a preimage we *must* somehow manage to propagate it to the upstream
- // channel, or we must have an ability to receive the same event and try
- // again on restart.
- log_error!(self.logger, "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
- payment_preimage, update_res);
- }
- // Note that we do *not* set `claimed_htlc` to false here. In fact, this
- // totally could be a duplicate claim, but we have no way of knowing
- // without interrogating the `ChannelMonitor` we've provided the above
- // update to. Instead, we simply document in `PaymentForwarded` that this
- // can happen.
- }
- mem::drop(channel_state_lock);
- if let ClaimFundsFromHop::MonitorUpdateFail(pk, err, _) = res {
+ let res = self.claim_funds_from_hop(channel_state_lock, hop_data, payment_preimage,
+ |htlc_claim_value_msat| {
+ if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
+ let fee_earned_msat = if let Some(claimed_htlc_value) = htlc_claim_value_msat {
+ Some(claimed_htlc_value - forwarded_htlc_value)
+ } else { None };
+
+ let prev_channel_id = Some(prev_outpoint.to_channel_id());
+ let next_channel_id = Some(next_channel_id);
+
+ Some(MonitorUpdateCompletionAction::EmitEvent { event: events::Event::PaymentForwarded {
+ fee_earned_msat,
+ claim_from_onchain_tx: from_onchain,
+ prev_channel_id,
+ next_channel_id,
+ }})
+ } else { None }
+ });
+ if let Err((pk, err)) = res {
let result: Result<(), _> = Err(err);
let _ = handle_error!(self, result, pk);
}
-
- if claimed_htlc {
- if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
- let fee_earned_msat = if let Some(claimed_htlc_value) = htlc_claim_value_msat {
- Some(claimed_htlc_value - forwarded_htlc_value)
- } else { None };
-
- let mut pending_events = self.pending_events.lock().unwrap();
- let prev_channel_id = Some(prev_outpoint.to_channel_id());
- let next_channel_id = Some(next_channel_id);
-
- pending_events.push(events::Event::PaymentForwarded {
- fee_earned_msat,
- claim_from_onchain_tx: from_onchain,
- prev_channel_id,
- next_channel_id,
- });
- }
- }
},
}
}
self.our_network_pubkey.clone()
}
+ fn handle_monitor_update_completion_actions<I: IntoIterator<Item=MonitorUpdateCompletionAction>>(&self, actions: I) {
+ for action in actions.into_iter() {
+ match action {
+ MonitorUpdateCompletionAction::PaymentClaimed { payment_hash } => {
+ let payment = self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
+ if let Some(ClaimingPayment { amount_msat, payment_purpose: purpose, receiver_node_id }) = payment {
+ self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
+ payment_hash, purpose, amount_msat, receiver_node_id: Some(receiver_node_id),
+ });
+ }
+ },
+ MonitorUpdateCompletionAction::EmitEvent { event } => {
+ self.pending_events.lock().unwrap().push(event);
+ },
+ }
+ }
+ }
+
/// 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>,
self.finalize_claims(finalized_claims);
for failure in pending_failures.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id: funding_txo.to_channel_id() };
- self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+ self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
}
}
if chan.get().get_counterparty_node_id() != *counterparty_node_id {
return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.temporary_channel_id));
}
- (try_chan_entry!(self, chan.get_mut().funding_created(msg, best_block, &self.logger), chan), chan.remove())
+ (try_chan_entry!(self, chan.get_mut().funding_created(msg, best_block, &self.keys_manager, &self.logger), chan), chan.remove())
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.temporary_channel_id))
}
if chan.get().get_counterparty_node_id() != *counterparty_node_id {
return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id));
}
- let (monitor, funding_tx, channel_ready) = match chan.get_mut().funding_signed(&msg, best_block, &self.logger) {
+ let (monitor, funding_tx, channel_ready) = match chan.get_mut().funding_signed(&msg, best_block, &self.keys_manager, &self.logger) {
Ok(update) => update,
Err(e) => try_chan_entry!(self, Err(e), chan),
};
};
for htlc_source in dropped_htlcs.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id: msg.channel_id };
- self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
}
let _ = handle_error!(self, result, *counterparty_node_id);
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
let reason = if (error_code & 0x1000) != 0 {
let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan);
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, real_code, &error_data)
+ HTLCFailReason::reason(real_code, error_data)
} else {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
- };
+ HTLCFailReason::from_failure_code(error_code)
+ }.get_encrypted_failure_packet(incoming_shared_secret, &None);
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,
htlc_id: msg.htlc_id,
if chan.get().get_counterparty_node_id() != *counterparty_node_id {
return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id));
}
- try_chan_entry!(self, chan.get_mut().update_fail_htlc(&msg, HTLCFailReason::LightningError { err: msg.reason.clone() }), chan);
+ try_chan_entry!(self, chan.get_mut().update_fail_htlc(&msg, HTLCFailReason::from_msg(msg)), chan);
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
}
let chan_err: ChannelError = ChannelError::Close("Got update_fail_malformed_htlc with BADONION not set".to_owned());
try_chan_entry!(self, Err(chan_err), chan);
}
- try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() }), chan);
+ try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::reason(msg.failure_code, msg.sha256_of_onion.to_vec())), chan);
Ok(())
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
});
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
+ HTLCFailReason::from_failure_code(0x4000 | 10),
HTLCDestination::InvalidForward { requested_forward_scid: scid },
));
}
}
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
- self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
}
if !new_intercept_events.is_empty() {
{
for failure in pending_failures.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() };
- self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+ self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
}
self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]);
self.finalize_claims(finalized_claim_htlcs);
} else {
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
- self.fail_htlc_backwards_internal(htlc_update.source, &htlc_update.payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
}
},
MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |
/// Check the holding cell in each channel and free any pending HTLCs in them if possible.
/// Returns whether there were any updates such as if pending HTLCs were freed or a monitor
/// update was applied.
- ///
- /// This should only apply to HTLCs which were added to the holding cell because we were
- /// waiting on a monitor update to finish. In that case, we don't want to free the holding cell
- /// directly in `channel_monitor_updated` as it may introduce deadlocks calling back into user
- /// code to inform them of a channel monitor update.
fn check_free_holding_cells(&self) -> bool {
let mut has_monitor_update = false;
let mut failed_htlcs = Vec::new();
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
///
- /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentReceived`], which
- /// will have the [`PaymentReceived::payment_preimage`] field filled in. That should then be
+ /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`], which
+ /// will have the [`PaymentClaimable::payment_preimage`] field filled in. That should then be
/// passed directly to [`claim_funds`].
///
/// See [`create_inbound_payment_for_hash`] for detailed documentation on behavior and requirements.
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// [`claim_funds`]: Self::claim_funds
- /// [`PaymentReceived`]: events::Event::PaymentReceived
- /// [`PaymentReceived::payment_preimage`]: events::Event::PaymentReceived::payment_preimage
+ /// [`PaymentClaimable`]: events::Event::PaymentClaimable
+ /// [`PaymentClaimable::payment_preimage`]: events::Event::PaymentClaimable::payment_preimage
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<(PaymentHash, PaymentSecret), ()> {
inbound_payment::create(&self.inbound_payment_key, min_value_msat, invoice_expiry_delta_secs, &self.keys_manager, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
///
- /// A [`PaymentReceived`] event will only be generated if the [`PaymentSecret`] matches a
+ /// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
/// the `min_value_msat` provided here, if one is provided.
///
///
/// `min_value_msat` should be set if the invoice being generated contains a value. Any payment
/// received for the returned [`PaymentHash`] will be required to be at least `min_value_msat`
- /// before a [`PaymentReceived`] event will be generated, ensuring that we do not provide the
+ /// before a [`PaymentClaimable`] event will be generated, ensuring that we do not provide the
/// sender "proof-of-payment" unless they have paid the required amount.
///
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
///
/// Note that we use block header time to time-out pending inbound payments (with some margin
/// to compensate for the inaccuracy of block header timestamps). Thus, in practice we will
- /// accept a payment and generate a [`PaymentReceived`] event for some time after the expiry.
+ /// accept a payment and generate a [`PaymentClaimable`] event for some time after the expiry.
/// If you need exact expiry semantics, you should enforce them upon receipt of
- /// [`PaymentReceived`].
+ /// [`PaymentClaimable`].
///
/// Note that invoices generated for inbound payments should have their `min_final_cltv_expiry`
/// set to at least [`MIN_FINAL_CLTV_EXPIRY`].
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
- /// [`PaymentReceived`]: events::Event::PaymentReceived
+ /// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, ()> {
inbound_payment::create_from_hash(&self.inbound_payment_key, min_value_msat, payment_hash, invoice_expiry_delta_secs, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
}
let mut inflight_htlcs = InFlightHtlcs::new();
for chan in self.channel_state.lock().unwrap().by_id.values() {
- for htlc_source in chan.inflight_htlc_sources() {
+ for (htlc_source, _) in chan.inflight_htlc_sources() {
if let HTLCSource::OutboundRoute { path, .. } = htlc_source {
inflight_htlcs.process_path(path, self.get_our_node_id());
}
events.into_inner()
}
+ #[cfg(test)]
+ pub fn pop_pending_event(&self) -> Option<events::Event> {
+ let mut events = self.pending_events.lock().unwrap();
+ if events.is_empty() { None } else { Some(events.remove(0)) }
+ }
+
#[cfg(test)]
pub fn has_pending_payments(&self) -> bool {
!self.pending_outbound_payments.lock().unwrap().is_empty()
if let Ok((channel_ready_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res {
for (source, payment_hash) in timed_out_pending_htlcs.drain(..) {
let (failure_code, data) = self.get_htlc_inbound_temp_fail_err_and_data(0x1000|14 /* expiry_too_soon */, &channel);
- timed_out_htlcs.push((source, payment_hash, HTLCFailReason::Reason {
- failure_code, data,
- }, HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
+ timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(failure_code, data),
+ HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
}
if let Some(channel_ready) = channel_ready_opt {
send_channel_ready!(self, pending_msg_events, channel, channel_ready);
}
if let Some(height) = height_opt {
- self.claimable_htlcs.lock().unwrap().retain(|payment_hash, (_, htlcs)| {
+ self.claimable_payments.lock().unwrap().claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
htlcs.retain(|htlc| {
// If height is approaching the number of blocks we think it takes us to get
// our commitment transaction confirmed before the HTLC expires, plus the
// number of blocks we generally consider it to take to do a commitment update,
// just give up on it and fail the HTLC.
if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER {
- let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
- htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
- timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
- failure_code: 0x4000 | 15,
- data: htlc_msat_height_data
- }, HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
+ let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
+ htlc_msat_height_data.extend_from_slice(&height.to_be_bytes());
+
+ timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(),
+ HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
+ HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
false
} else { true }
});
_ => unreachable!(),
};
timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
- HTLCFailReason::Reason { failure_code: 0x2000 | 2, data: Vec::new() },
+ HTLCFailReason::from_failure_code(0x2000 | 2),
HTLCDestination::InvalidForward { requested_forward_scid }));
log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
false
self.handle_init_event_channel_failures(failed_channels);
for (source, payment_hash, reason, destination) in timed_out_htlcs.drain(..) {
- self.fail_htlc_backwards_internal(source, &payment_hash, reason, destination);
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, destination);
}
}
}
}
-impl_writeable_tlv_based_enum!(HTLCFailReason,
- (0, LightningError) => {
- (0, err, required),
- },
- (1, Reason) => {
- (0, failure_code, required),
- (2, data, vec_type),
- },
-;);
-
impl_writeable_tlv_based!(PendingAddHTLCInfo, {
(0, forward_info, required),
(1, prev_user_channel_id, (default_value, 0)),
}
let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
- let claimable_htlcs = self.claimable_htlcs.lock().unwrap();
+ let claimable_payments = self.claimable_payments.lock().unwrap();
let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
let mut htlc_purposes: Vec<&events::PaymentPurpose> = Vec::new();
- (claimable_htlcs.len() as u64).write(writer)?;
- for (payment_hash, (purpose, previous_hops)) in claimable_htlcs.iter() {
+ (claimable_payments.claimable_htlcs.len() as u64).write(writer)?;
+ for (payment_hash, (purpose, previous_hops)) in claimable_payments.claimable_htlcs.iter() {
payment_hash.write(writer)?;
(previous_hops.len() as u64).write(writer)?;
for htlc in previous_hops.iter() {
if our_pending_intercepts.len() != 0 {
pending_intercepted_htlcs = Some(our_pending_intercepts);
}
+
+ let mut pending_claiming_payments = Some(&claimable_payments.pending_claiming_payments);
+ if pending_claiming_payments.as_ref().unwrap().is_empty() {
+ // LDK versions prior to 0.0.113 do not know how to read the pending claimed payments
+ // map. Thus, if there are no entries we skip writing a TLV for it.
+ pending_claiming_payments = None;
+ } else {
+ debug_assert!(false, "While we have code to serialize pending_claiming_payments, the map should always be empty until a later PR");
+ }
+
write_tlv_fields!(writer, {
(1, pending_outbound_payments_no_retry, required),
(2, pending_intercepted_htlcs, option),
(3, pending_outbound_payments, required),
+ (4, pending_claiming_payments, option),
(5, self.our_network_pubkey, required),
(7, self.fake_scid_rand_bytes, required),
(9, htlc_purposes, vec_type),
user_channel_id: channel.get_user_id(),
reason: ClosureReason::OutdatedChannelManager
});
+ for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() {
+ let mut found_htlc = false;
+ for (monitor_htlc_source, _) in monitor.get_all_current_outbound_htlcs() {
+ if *channel_htlc_source == monitor_htlc_source { found_htlc = true; break; }
+ }
+ if !found_htlc {
+ // If we have some HTLCs in the channel which are not present in the newer
+ // ChannelMonitor, they have been removed and should be failed back to
+ // ensure we don't forget them entirely. Note that if the missing HTLC(s)
+ // were actually claimed we'd have generated and ensured the previous-hop
+ // claim update ChannelMonitor updates were persisted prior to persising
+ // the ChannelMonitor update for the forward leg, so attempting to fail the
+ // 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.channel_id()), log_bytes!(payment_hash.0));
+ failed_htlcs.push((channel_htlc_source.clone(), *payment_hash, channel.get_counterparty_node_id(), channel.channel_id()));
+ }
+ }
} else {
log_info!(args.logger, "Successfully loaded channel {}", log_bytes!(channel.channel_id()));
if let Some(short_channel_id) = channel.get_short_channel_id() {
None => continue,
}
}
- if forward_htlcs_count > 0 {
- // If we have pending HTLCs to forward, assume we either dropped a
- // `PendingHTLCsForwardable` or the user received it but never processed it as they
- // shut down before the timer hit. Either way, set the time_forwardable to a small
- // constant as enough time has likely passed that we should simply handle the forwards
- // now, or at least after the user gets a chance to reconnect to our peers.
- pending_events_read.push(events::Event::PendingHTLCsForwardable {
- time_forwardable: Duration::from_secs(2),
- });
- }
let background_event_count: u64 = Readable::read(reader)?;
let mut pending_background_events_read: Vec<BackgroundEvent> = Vec::with_capacity(cmp::min(background_event_count as usize, MAX_ALLOC_SIZE/mem::size_of::<BackgroundEvent>()));
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
let mut probing_cookie_secret: Option<[u8; 32]> = None;
let mut claimable_htlc_purposes = None;
+ let mut pending_claiming_payments = Some(HashMap::new());
read_tlv_fields!(reader, {
(1, pending_outbound_payments_no_retry, option),
(2, pending_intercepted_htlcs, option),
(3, pending_outbound_payments, option),
+ (4, pending_claiming_payments, option),
(5, received_network_pubkey, option),
(7, fake_scid_rand_bytes, option),
(9, claimable_htlc_purposes, vec_type),
}
}
}
+ for (htlc_source, htlc) in monitor.get_all_current_outbound_htlcs() {
+ if let HTLCSource::PreviousHopData(prev_hop_data) = htlc_source {
+ // The ChannelMonitor is now responsible for this HTLC's
+ // failure/success and will let us know what its outcome is. If we
+ // still have an entry for this HTLC in `forward_htlcs`, we were
+ // apparently not persisted after the monitor was when forwarding
+ // the payment.
+ forward_htlcs.retain(|_, forwards| {
+ forwards.retain(|forward| {
+ if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
+ if htlc_info.prev_short_channel_id == prev_hop_data.short_channel_id &&
+ htlc_info.prev_htlc_id == prev_hop_data.htlc_id
+ {
+ 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()));
+ false
+ } else { true }
+ } else { true }
+ });
+ !forwards.is_empty()
+ })
+ }
+ }
}
}
}
+ if !forward_htlcs.is_empty() {
+ // If we have pending HTLCs to forward, assume we either dropped a
+ // `PendingHTLCsForwardable` or the user received it but never processed it as they
+ // shut down before the timer hit. Either way, set the time_forwardable to a small
+ // constant as enough time has likely passed that we should simply handle the forwards
+ // now, or at least after the user gets a chance to reconnect to our peers.
+ pending_events_read.push(events::Event::PendingHTLCsForwardable {
+ time_forwardable: Duration::from_secs(2),
+ });
+ }
+
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
forward_htlcs: Mutex::new(forward_htlcs),
- claimable_htlcs: Mutex::new(claimable_htlcs),
+ claimable_payments: Mutex::new(ClaimablePayments { claimable_htlcs, pending_claiming_payments: pending_claiming_payments.unwrap() }),
outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
id_to_peer: Mutex::new(id_to_peer),
short_to_chan_info: FairRwLock::new(short_to_chan_info),
for htlc_source in failed_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
- channel_manager.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ channel_manager.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
//TODO: Broadcast channel update for closed channels, but only after we've made a
$node_b.handle_revoke_and_ack(&$node_a.get_our_node_id(), &get_event_msg!(NodeHolder { node: &$node_a }, MessageSendEvent::SendRevokeAndACK, $node_b.get_our_node_id()));
expect_pending_htlcs_forwardable!(NodeHolder { node: &$node_b });
- expect_payment_received!(NodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
+ expect_payment_claimable!(NodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
$node_b.claim_funds(payment_preimage);
expect_payment_claimed!(NodeHolder { node: &$node_b }, payment_hash, 10_000);
BasicMPP,
]);
define_context!(OfferContext, []);
+ define_context!(InvoiceRequestContext, []);
// This isn't a "real" feature context, and is only used in the channel_type field in an
// `OpenChannel` message.
define_context!(ChannelTypeContext, [
supports_keysend, requires_keysend);
#[cfg(test)]
- define_feature!(123456789, UnknownFeature, [NodeContext, ChannelContext, InvoiceContext, OfferContext],
+ define_feature!(123456789, UnknownFeature,
+ [NodeContext, ChannelContext, InvoiceContext, OfferContext, InvoiceRequestContext],
"Feature flags for an unknown feature used in testing.", set_unknown_feature_optional,
set_unknown_feature_required, supports_unknown_test_feature, requires_unknown_test_feature);
}
pub type ChannelFeatures = Features<sealed::ChannelContext>;
/// Features used within an invoice.
pub type InvoiceFeatures = Features<sealed::InvoiceContext>;
-/// Features used within an offer.
+/// Features used within an `offer`.
pub type OfferFeatures = Features<sealed::OfferContext>;
+/// Features used within an `invoice_request`.
+pub type InvoiceRequestFeatures = Features<sealed::InvoiceRequestContext>;
/// Features used within the channel_type field in an OpenChannel message.
///
impl_feature_tlv_write!(ChannelTypeFeatures);
impl_feature_tlv_write!(OfferFeatures);
+impl_feature_tlv_write!(InvoiceRequestFeatures);
#[cfg(test)]
mod tests {
use $crate::util::events::Event;
let events = $node.node.get_and_clear_pending_events();
- assert_eq!(events.len(), $events);
+ assert_eq!(events.len(), $events, "{:?}", events);
let expected_reason = $reason;
let mut issues_discard_funding = false;
for event in events {
let events = $node.node.get_and_clear_pending_events();
match events[0] {
$crate::util::events::Event::PendingHTLCsForwardable { .. } => { },
- _ => panic!("Unexpected event"),
+ _ => panic!("Unexpected event {:?}", events),
};
let count = expected_failures.len() + 1;
}
#[macro_export]
#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
-macro_rules! expect_payment_received {
+macro_rules! expect_payment_claimable {
($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr) => {
- expect_payment_received!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None, $node.node.get_our_node_id())
+ expect_payment_claimable!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None, $node.node.get_our_node_id())
};
($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr, $expected_payment_preimage: expr, $expected_receiver_node_id: expr) => {
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id: _, via_user_channel_id: _ } => {
+ $crate::util::events::Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id: _, via_user_channel_id: _ } => {
assert_eq!($expected_payment_hash, *payment_hash);
assert_eq!($expected_recv_value, amount_msat);
assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap());
if !$downstream_force_closed {
assert!($node.node.list_channels().iter().any(|x| x.counterparty.node_id == $next_node.node.get_our_node_id() && x.channel_id == next_channel_id.unwrap()));
}
- assert_eq!(claim_from_onchain_tx, $upstream_force_closed);
+ assert_eq!(claim_from_onchain_tx, $downstream_force_closed);
},
_ => panic!("Unexpected event"),
}
payment_id
}
-pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_received_expected: bool, clear_recipient_events: bool, expected_preimage: Option<PaymentPreimage>) {
+pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option<PaymentPreimage>) {
let mut payment_event = SendEvent::from_event(ev);
let mut prev_node = origin_node;
if idx == expected_path.len() - 1 && clear_recipient_events {
let events_2 = node.node.get_and_clear_pending_events();
- if payment_received_expected {
+ if payment_claimable_expected {
assert_eq!(events_2.len(), 1);
match events_2[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
assert_eq!(our_payment_hash, *payment_hash);
assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
match &purpose {
}
}
-pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_received_expected: bool, expected_preimage: Option<PaymentPreimage>) {
- do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_received_expected, true, expected_preimage);
+pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, expected_preimage: Option<PaymentPreimage>) {
+ do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage);
}
pub fn pass_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) {
assert_eq!(events.len(), expected_route.len());
for (path_idx, (ev, expected_path)) in events.drain(..).zip(expected_route.iter()).enumerate() {
// Once we've gotten through all the HTLCs, the last one should result in a
- // PaymentReceived (but each previous one should not!), .
+ // PaymentClaimable (but each previous one should not!), .
let expect_payment = path_idx == expected_route.len() - 1;
pass_along_path(origin_node, expected_path, recv_value, our_payment_hash.clone(), Some(our_payment_secret), ev, expect_payment, None);
}
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::enforcing_trait_impls::EnforcingSigner;
-use crate::util::{byte_utils, test_utils};
+use crate::util::test_utils;
use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason, HTLCDestination};
use crate::util::errors::APIError;
use crate::util::ser::{Writeable, ReadableArgs};
// Assemble the set of keys we can use for signatures for our commitment_signed message.
let commit_tx_keys = chan_utils::TxCreationKeys::derive_new(&secp_ctx, &remote_point, &remote_delayed_payment_basepoint,
- &remote_htlc_basepoint, &local_revocation_basepoint, &local_htlc_basepoint).unwrap();
+ &remote_htlc_basepoint, &local_revocation_basepoint, &local_htlc_basepoint);
let res = {
let local_chan_lock = nodes[0].node.channel_state.lock().unwrap();
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { .. } => { },
+ Event::PaymentClaimable { .. } => { },
_ => panic!("Unexpected event"),
};
assert_eq!(events.len(), payments.len());
for (event, &(_, ref hash)) in events.iter().zip(payments.iter()) {
match event {
- &Event::PaymentReceived { ref payment_hash, .. } => {
+ &Event::PaymentClaimable { ref payment_hash, .. } => {
assert_eq!(*payment_hash, *hash);
},
_ => panic!("Unexpected event"),
// Assemble the set of keys we can use for signatures for our commitment_signed message.
let commit_tx_keys = chan_utils::TxCreationKeys::derive_new(&secp_ctx, &remote_point, &remote_delayed_payment_basepoint,
- &remote_htlc_basepoint, &local_revocation_basepoint, &local_htlc_basepoint).unwrap();
+ &remote_htlc_basepoint, &local_revocation_basepoint, &local_htlc_basepoint);
// Build the remote commitment transaction so we can sign it, and then later use the
// signature for the commitment_signed message.
commitment_signed_dance!(nodes[2], nodes[1], payment_event_11.commitment_msg, false);
expect_pending_htlcs_forwardable!(nodes[2]);
- expect_payment_received!(nodes[2], our_payment_hash_1, our_payment_secret_1, recv_value_1);
+ expect_payment_claimable!(nodes[2], our_payment_hash_1, our_payment_secret_1, recv_value_1);
// flush the htlcs in the holding cell
assert_eq!(commitment_update_2.update_add_htlcs.len(), 2);
let events = nodes[2].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match events[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(our_payment_hash_21, *payment_hash);
assert_eq!(recv_value_21, amount_msat);
assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
_ => panic!("Unexpected event"),
}
match events[1] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(our_payment_hash_22, *payment_hash);
assert_eq!(recv_value_22, amount_msat);
assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_3, payment_secret_3, 100000);
+ expect_payment_claimable!(nodes[1], payment_hash_3, payment_secret_3, 100000);
// Note that as this RAA was generated before the delivery of the update_fulfill it shouldn't
// resolve the second HTLC from A's point of view.
check_added_monitors!(nodes[0], 1);
expect_pending_htlcs_forwardable!(nodes[0]);
- expect_payment_received!(nodes[0], payment_hash_4, payment_secret_4, 10000);
+ expect_payment_claimable!(nodes[0], payment_hash_4, payment_secret_4, 10000);
claim_payment(&nodes[1], &[&nodes[0]], payment_preimage_4);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_3);
let events_2 = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events_2.len(), 1);
match events_2[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
assert_eq!(payment_hash_1, *payment_hash);
assert_eq!(amount_msat, 1_000_000);
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
let events_5 = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events_5.len(), 1);
match events_5[0] {
- Event::PaymentReceived { ref payment_hash, ref purpose, .. } => {
+ Event::PaymentClaimable { ref payment_hash, ref purpose, .. } => {
assert_eq!(payment_hash_2, *payment_hash);
match &purpose {
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
// Now do the relevant commitment_signed/RAA dances along the path, noting that the final
- // hop should *not* yet generate any PaymentReceived event(s).
+ // hop should *not* yet generate any PaymentClaimable event(s).
pass_along_path(&nodes[0], &[&nodes[1]], 100000, our_payment_hash, Some(payment_secret), events.drain(..).next().unwrap(), false, None);
our_payment_hash
} else {
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
// 100_000 msat as u64, followed by the height at which we failed back above
- let mut expected_failure_data = byte_utils::be64_to_array(100_000).to_vec();
- expected_failure_data.extend_from_slice(&byte_utils::be32_to_array(block_count - 1));
+ let mut expected_failure_data = (100_000 as u64).to_be_bytes().to_vec();
+ expected_failure_data.extend_from_slice(&(block_count - 1).to_be_bytes());
expect_payment_failed!(nodes[0], our_payment_hash, true, 0x4000 | 15, &expected_failure_data[..]);
}
assert_eq!(htlc_success_txn[2], commitment_txn[0]);
assert_eq!(htlc_success_txn[3], htlc_success_txn[0]);
assert_eq!(htlc_success_txn[4], htlc_success_txn[1]);
- assert_ne!(htlc_success_txn[0].input[0].previous_output, htlc_timeout_tx.input[0].previous_output);
+ assert_ne!(htlc_success_txn[1].input[0].previous_output, htlc_timeout_tx.input[0].previous_output);
mine_transaction(&nodes[1], &htlc_timeout_tx);
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
// Solve 2nd HTLC by broadcasting on B's chain HTLC-Success Tx from C
// Note that the fee paid is effectively double as the HTLC value (including the nodes[1] fee
// and nodes[2] fee) is rounded down and then claimed in full.
- mine_transaction(&nodes[1], &htlc_success_txn[0]);
+ mine_transaction(&nodes[1], &htlc_success_txn[1]);
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(196*2), true, true);
let updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
assert!(updates.update_add_htlcs.is_empty());
#[test]
fn test_key_derivation_params() {
- // This test is a copy of test_dynamic_spendable_outputs_local_htlc_timeout_tx, with
- // a key manager rotation to test that key_derivation_params returned in DynamicOutputP2WSH
- // let us re-derive the channel key set to then derive a delayed_payment_key.
+ // This test is a copy of test_dynamic_spendable_outputs_local_htlc_timeout_tx, with a key
+ // manager rotation to test that `channel_keys_id` returned in
+ // [`SpendableOutputDescriptor::DelayedPaymentOutput`] let us re-derive the channel key set to
+ // then derive a `delayed_payment_key`.
let chanmon_cfgs = create_chanmon_cfgs(3);
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { .. } => {},
+ Event::PaymentClaimable { .. } => {},
_ => panic!("Unexpected event"),
}
nodes[1].node.claim_funds(payment_preimage_1);
.with_features(channelmanager::provided_invoice_features());
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000, 0);
route.paths[0].last_mut().unwrap().cltv_expiry_delta = 500000001;
- unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::RouteError { ref err },
+ unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::InvalidRoute { ref err },
assert_eq!(err, &"Channel CLTV overflowed?"));
}
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], our_payment_hash, our_payment_secret, 100000);
+ expect_payment_claimable!(nodes[1], our_payment_hash, our_payment_secret, 100000);
}
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100000);
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::ChannelUnavailable { ref err },
commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false, true);
// 10_000 msat as u64, followed by a height of CHAN_CONFIRM_DEPTH as u32
- let mut expected_failure_data = byte_utils::be64_to_array(10_000).to_vec();
- expected_failure_data.extend_from_slice(&byte_utils::be32_to_array(CHAN_CONFIRM_DEPTH));
+ let mut expected_failure_data = (10_000 as u64).to_be_bytes().to_vec();
+ expected_failure_data.extend_from_slice(&CHAN_CONFIRM_DEPTH.to_be_bytes());
expect_payment_failed!(nodes[0], our_payment_hash, true, 0x4000|15, &expected_failure_data[..]);
}
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { ref purpose, .. } => {
+ Event::PaymentClaimable { ref purpose, .. } => {
match &purpose {
PaymentPurpose::InvoicePayment { payment_preimage, .. } => {
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage.unwrap());
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentReceived { purpose: PaymentPurpose::InvoicePayment { payment_preimage, payment_secret }, .. } => {
+ Event::PaymentClaimable { purpose: PaymentPurpose::InvoicePayment { payment_preimage, payment_secret }, .. } => {
assert!(payment_preimage.is_none());
assert_eq!(payment_secret, our_payment_secret);
// We don't actually have the payment preimage with which to claim this payment!
let mut w = test_utils::TestVecWriter(Vec::new());
monitor.write(&mut w).unwrap();
let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingSigner>)>::read(
- &mut io::Cursor::new(&w.0), &test_utils::OnlyReadsKeysInterface {}).unwrap().1;
+ &mut io::Cursor::new(&w.0), nodes[0].keys_manager).unwrap().1;
assert!(new_monitor == *monitor);
let watchtower = test_utils::TestChainMonitor::new(Some(&chain_source), &chanmon_cfgs[0].tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator, &persister, &node_cfgs[0].keys_manager);
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
let mut w = test_utils::TestVecWriter(Vec::new());
monitor.write(&mut w).unwrap();
let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingSigner>)>::read(
- &mut io::Cursor::new(&w.0), &test_utils::OnlyReadsKeysInterface {}).unwrap().1;
+ &mut io::Cursor::new(&w.0), nodes[0].keys_manager).unwrap().1;
assert!(new_monitor == *monitor);
let watchtower = test_utils::TestChainMonitor::new(Some(&chain_source), &chanmon_cfgs[0].tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator, &persister, &node_cfgs[0].keys_manager);
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
let mut w = test_utils::TestVecWriter(Vec::new());
monitor.write(&mut w).unwrap();
let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingSigner>)>::read(
- &mut io::Cursor::new(&w.0), &test_utils::OnlyReadsKeysInterface {}).unwrap().1;
+ &mut io::Cursor::new(&w.0), nodes[0].keys_manager).unwrap().1;
assert!(new_monitor == *monitor);
let watchtower = test_utils::TestChainMonitor::new(Some(&chain_source), &chanmon_cfgs[0].tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator, &persister, &node_cfgs[0].keys_manager);
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
}
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], our_payment_hash, our_payment_secret, 10_000);
+ expect_payment_claimable!(nodes[1], our_payment_hash, our_payment_secret, 10_000);
{
// Note that we use a different PaymentId here to allow us to duplicativly pay
#[test]
fn test_dup_htlc_second_fail_panic() {
// Previously, if we received two HTLCs back-to-back, where the second overran the expected
- // value for the payment, we'd fail back both HTLCs after generating a `PaymentReceived` event.
+ // value for the payment, we'd fail back both HTLCs after generating a `PaymentClaimable` event.
// Then, if the user failed the second payment, they'd hit a "tried to fail an already failed
// HTLC" debug panic. This tests for this behavior, checking that only one HTLC is auto-failed.
do_test_dup_htlc_second_rejected(true);
#[test]
fn test_double_partial_claim() {
- // Test what happens if a node receives a payment, generates a PaymentReceived event, the HTLCs
+ // Test what happens if a node receives a payment, generates a PaymentClaimable event, the HTLCs
// time out, the sender resends only some of the MPP parts, then the user processes the
- // PaymentReceived event, ensuring they don't inadvertently claim only part of the full payment
+ // PaymentClaimable event, ensuring they don't inadvertently claim only part of the full payment
// amount.
let chanmon_cfgs = create_chanmon_cfgs(4);
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
});
send_along_route_with_secret(&nodes[0], route.clone(), &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 15_000_000, payment_hash, payment_secret);
- // nodes[3] has now received a PaymentReceived event...which it will take some (exorbitant)
+ // nodes[3] has now received a PaymentClaimable event...which it will take some (exorbitant)
// amount of time to respond to.
// Connect some blocks to time out the payment
pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 15_000_000, payment_hash, Some(payment_secret), events.drain(..).next().unwrap(), false, None);
// At this point nodes[3] has received one half of the payment, and the user goes to handle
- // that PaymentReceived event they got hours ago and never handled...we should refuse to claim.
+ // that PaymentClaimable event they got hours ago and never handled...we should refuse to claim.
nodes[3].node.claim_funds(payment_preimage);
check_added_monitors!(nodes[3], 0);
assert!(nodes[3].node.get_and_clear_pending_msg_events().is_empty());
commitment_signed_dance!(nodes[1], nodes[0], updates.commitment_signed, false);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash, payment_secret, 10_000_000);
+ expect_payment_claimable!(nodes[1], payment_hash, payment_secret, 10_000_000);
let (route_2, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 20_000_000);
nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap();
commitment_signed_dance!(nodes[1], nodes[0], updates.commitment_signed, false);
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 20_000_000);
+ expect_payment_claimable!(nodes[1], payment_hash_2, payment_secret_2, 20_000_000);
nodes[1].node.claim_funds(payment_preimage_2);
get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);
use crate::ln::wire::Encode;
use crate::util::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::util::ser::{Writeable, Writer};
-use crate::util::{byte_utils, test_utils};
+use crate::util::test_utils;
use crate::util::config::{UserConfig, ChannelConfig};
use crate::util::errors::APIError;
if test_case == 2 || test_case == 200 {
expect_htlc_forward!(&nodes[2]);
- expect_event!(&nodes[2], Event::PaymentReceived);
+ expect_event!(&nodes[2], Event::PaymentClaimable);
callback_node();
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[2], vec![HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }]);
}
connect_blocks(&nodes[0], height - nodes[0].best_block_info().1);
connect_blocks(&nodes[1], height - nodes[1].best_block_info().1);
connect_blocks(&nodes[2], height - nodes[2].best_block_info().1);
- }, || {}, true, Some(17), None, None);
+ }, || {}, false, Some(0x4000 | 15), None, None);
run_onion_failure_test("final_incorrect_cltv_expiry", 1, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
for (_, pending_forwards) in nodes[1].node.forward_htlcs.lock().unwrap().iter_mut() {
commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
// Ensure the payment fails with the expected error.
- let expected_cltv = 82;
- let error_data = byte_utils::be32_to_array(expected_cltv).to_vec();
+ let expected_cltv: u32 = 82;
+ let error_data = expected_cltv.to_be_bytes().to_vec();
let mut fail_conditions = PaymentFailedConditions::new()
.blamed_scid(phantom_scid)
.expected_htlc_error_data(18, &error_data);
commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
// Ensure the payment fails with the expected error.
- let error_data = Vec::new();
+ let mut error_data = recv_value_msat.to_be_bytes().to_vec();
+ error_data.extend_from_slice(
+ &nodes[0].node.best_block.read().unwrap().height().to_be_bytes(),
+ );
+ let mut fail_conditions = PaymentFailedConditions::new()
+ .blamed_scid(phantom_scid)
+ .expected_htlc_error_data(0x4000 | 15, &error_data);
+ expect_payment_failed_conditions(&nodes[0], payment_hash, true, fail_conditions);
+}
+
+#[test]
+fn test_phantom_failure_modified_cltv() {
+ // Test that we fail back phantoms if the upstream node fiddled with the CLTV too much with the
+ // correct error code.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let channel = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());
+
+ // Get the route.
+ let recv_value_msat = 10_000;
+ let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[1], Some(recv_value_msat));
+ let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel);
+
+ // Route the HTLC through to the destination.
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let update_0 = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ let mut update_add = update_0.update_add_htlcs[0].clone();
+
+ // Modify the route to have a too-low cltv.
+ update_add.cltv_expiry -= 10;
+
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
+ commitment_signed_dance!(nodes[1], nodes[0], &update_0.commitment_signed, false, true);
+
+ let update_1 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ assert!(update_1.update_fail_htlcs.len() == 1);
+ let fail_msg = update_1.update_fail_htlcs[0].clone();
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
+ commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
+
+ // Ensure the payment fails with the expected error.
let mut fail_conditions = PaymentFailedConditions::new()
.blamed_scid(phantom_scid)
- .expected_htlc_error_data(17, &error_data);
+ .expected_htlc_error_data(0x2000 | 2, &[]);
+ expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
+}
+
+#[test]
+fn test_phantom_failure_expires_too_soon() {
+ // Test that we fail back phantoms if the HTLC got delayed and we got blocks in between with
+ // the correct error code.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let channel = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());
+
+ // Get the route.
+ let recv_value_msat = 10_000;
+ let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[1], Some(recv_value_msat));
+ let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel);
+
+ // Route the HTLC through to the destination.
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let update_0 = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ let mut update_add = update_0.update_add_htlcs[0].clone();
+
+ connect_blocks(&nodes[1], CLTV_FAR_FAR_AWAY);
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
+ commitment_signed_dance!(nodes[1], nodes[0], &update_0.commitment_signed, false, true);
+
+ let update_1 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ assert!(update_1.update_fail_htlcs.len() == 1);
+ let fail_msg = update_1.update_fail_htlcs[0].clone();
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
+ commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
+
+ // Ensure the payment fails with the expected error.
+ let mut fail_conditions = PaymentFailedConditions::new()
+ .blamed_scid(phantom_scid)
+ .expected_htlc_error_data(0x2000 | 2, &[]);
expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
}
commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
// Ensure the payment fails with the expected error.
- let mut error_data = byte_utils::be64_to_array(bad_recv_amt_msat).to_vec();
- error_data.extend_from_slice(
- &byte_utils::be32_to_array(nodes[1].node.best_block.read().unwrap().height()),
- );
+ let mut error_data = bad_recv_amt_msat.to_be_bytes().to_vec();
+ error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height().to_be_bytes());
let mut fail_conditions = PaymentFailedConditions::new()
.blamed_scid(phantom_scid)
.expected_htlc_error_data(0x4000 | 15, &error_data);
nodes[1].node.process_pending_htlc_forwards();
expect_pending_htlcs_forwardable_ignore!(nodes[1]);
nodes[1].node.process_pending_htlc_forwards();
- expect_payment_received!(nodes[1], payment_hash, payment_secret, recv_amt_msat, None, route.paths[0].last().unwrap().pubkey);
+ expect_payment_claimable!(nodes[1], payment_hash, payment_secret, recv_amt_msat, None, route.paths[0].last().unwrap().pubkey);
nodes[1].node.fail_htlc_backwards(&payment_hash);
expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::FailedPayment { payment_hash }]);
nodes[1].node.process_pending_htlc_forwards();
commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
// Ensure the payment fails with the expected error.
- let mut error_data = byte_utils::be64_to_array(recv_amt_msat).to_vec();
- error_data.extend_from_slice(
- &byte_utils::be32_to_array(nodes[1].node.best_block.read().unwrap().height()),
- );
+ let mut error_data = recv_amt_msat.to_be_bytes().to_vec();
+ error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height().to_be_bytes());
let mut fail_conditions = PaymentFailedConditions::new()
.blamed_scid(phantom_scid)
.expected_htlc_error_data(0x4000 | 15, &error_data);
use crate::routing::router::RouteHop;
use crate::util::chacha20::{ChaCha20, ChaChaReader};
use crate::util::errors::{self, APIError};
-use crate::util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter};
+use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter};
use crate::util::logger::Logger;
use bitcoin::hashes::{Hash, HashEngine};
});
cur_value_msat += hop.fee_msat;
if cur_value_msat >= 21000000 * 100000000 * 1000 {
- return Err(APIError::RouteError{err: "Channel fees overflowed?"});
+ return Err(APIError::InvalidRoute{err: "Channel fees overflowed?"});
}
cur_cltv += hop.cltv_expiry_delta as u32;
if cur_cltv >= 500000000 {
- return Err(APIError::RouteError{err: "Channel CLTV overflowed?"});
+ return Err(APIError::InvalidRoute{err: "Channel CLTV overflowed?"});
}
last_short_channel_id = hop.short_channel_id;
}
packet
}
-#[inline]
+#[cfg(test)]
pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type: u16, failure_data: &[u8]) -> msgs::OnionErrorPacket {
let failure_packet = build_failure_packet(shared_secret, failure_type, failure_data);
encrypt_failure_packet(shared_secret, &failure_packet.encode()[..])
} else { unreachable!(); }
}
+#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
+
+#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+enum HTLCFailReasonRepr {
+ LightningError {
+ err: msgs::OnionErrorPacket,
+ },
+ Reason {
+ failure_code: u16,
+ data: Vec<u8>,
+ }
+}
+
+impl core::fmt::Debug for HTLCFailReason {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ match self.0 {
+ HTLCFailReasonRepr::Reason { ref failure_code, .. } => {
+ write!(f, "HTLC error code {}", failure_code)
+ },
+ HTLCFailReasonRepr::LightningError { .. } => {
+ write!(f, "pre-built LightningError")
+ }
+ }
+ }
+}
+
+impl Writeable for HTLCFailReason {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
+ self.0.write(writer)
+ }
+}
+impl Readable for HTLCFailReason {
+ fn read<R: Read>(reader: &mut R) -> Result<Self, msgs::DecodeError> {
+ Ok(Self(Readable::read(reader)?))
+ }
+}
+
+impl_writeable_tlv_based_enum!(HTLCFailReasonRepr,
+ (0, LightningError) => {
+ (0, err, required),
+ },
+ (1, Reason) => {
+ (0, failure_code, required),
+ (2, data, vec_type),
+ },
+;);
+
+impl HTLCFailReason {
+ pub(super) fn reason(failure_code: u16, data: Vec<u8>) -> Self {
+ const BADONION: u16 = 0x8000;
+ const PERM: u16 = 0x4000;
+ const NODE: u16 = 0x2000;
+ const UPDATE: u16 = 0x1000;
+
+ if failure_code == 1 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 2 | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 2 | PERM | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 3 | PERM | NODE { debug_assert!(data.is_empty()) }
+ else if failure_code == 4 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 5 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 6 | BADONION | PERM { debug_assert_eq!(data.len(), 32) }
+ else if failure_code == 7 | UPDATE {
+ debug_assert_eq!(data.len() - 2, u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize) }
+ else if failure_code == 8 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 9 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 10 | PERM { debug_assert!(data.is_empty()) }
+ else if failure_code == 11 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 8, u16::from_be_bytes(data[8..10].try_into().unwrap()) as usize) }
+ else if failure_code == 12 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 8, u16::from_be_bytes(data[8..10].try_into().unwrap()) as usize) }
+ else if failure_code == 13 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 4, u16::from_be_bytes(data[4..6].try_into().unwrap()) as usize) }
+ else if failure_code == 14 | UPDATE {
+ debug_assert_eq!(data.len() - 2, u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize) }
+ else if failure_code == 15 | PERM { debug_assert_eq!(data.len(), 12) }
+ else if failure_code == 18 { debug_assert_eq!(data.len(), 4) }
+ else if failure_code == 19 { debug_assert_eq!(data.len(), 8) }
+ else if failure_code == 20 | UPDATE {
+ debug_assert_eq!(data.len() - 2 - 2, u16::from_be_bytes(data[2..4].try_into().unwrap()) as usize) }
+ else if failure_code == 21 { debug_assert!(data.is_empty()) }
+ else if failure_code == 22 | PERM { debug_assert!(data.len() <= 11) }
+ else if failure_code == 23 { debug_assert!(data.is_empty()) }
+ else if failure_code & BADONION != 0 {
+ // We set some bogus BADONION failure codes in test, so ignore unknown ones.
+ }
+ else { debug_assert!(false, "Unknown failure code: {}", failure_code) }
+
+ Self(HTLCFailReasonRepr::Reason { failure_code, data })
+ }
+
+ pub(super) fn from_failure_code(failure_code: u16) -> Self {
+ Self::reason(failure_code, Vec::new())
+ }
+
+ pub(super) fn from_msg(msg: &msgs::UpdateFailHTLC) -> Self {
+ Self(HTLCFailReasonRepr::LightningError { err: msg.reason.clone() })
+ }
+
+ pub(super) fn get_encrypted_failure_packet(&self, incoming_packet_shared_secret: &[u8; 32], phantom_shared_secret: &Option<[u8; 32]>)
+ -> msgs::OnionErrorPacket {
+ match self.0 {
+ HTLCFailReasonRepr::Reason { ref failure_code, ref data } => {
+ if let Some(phantom_ss) = phantom_shared_secret {
+ let phantom_packet = build_failure_packet(phantom_ss, *failure_code, &data[..]).encode();
+ let encrypted_phantom_packet = encrypt_failure_packet(phantom_ss, &phantom_packet);
+ encrypt_failure_packet(incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
+ } else {
+ let packet = build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..]).encode();
+ encrypt_failure_packet(incoming_packet_shared_secret, &packet)
+ }
+ },
+ HTLCFailReasonRepr::LightningError { ref err } => {
+ encrypt_failure_packet(incoming_packet_shared_secret, &err.data)
+ }
+ }
+ }
+
+ pub(super) fn decode_onion_failure<T: secp256k1::Signing, L: Deref>(
+ &self, secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource
+ ) -> (Option<NetworkUpdate>, Option<u64>, bool, Option<u16>, Option<Vec<u8>>)
+ where L::Target: Logger {
+ match self.0 {
+ HTLCFailReasonRepr::LightningError { ref err } => {
+ process_onion_failure(secp_ctx, logger, &htlc_source, err.data.clone())
+ },
+ HTLCFailReasonRepr::Reason { ref failure_code, ref data, .. } => {
+ // we get a fail_malformed_htlc from the first hop
+ // TODO: We'd like to generate a NetworkUpdate for temporary
+ // failures here, but that would be insufficient as find_route
+ // generally ignores its view of our own channels as we provide them via
+ // ChannelDetails.
+ if let &HTLCSource::OutboundRoute { ref path, .. } = htlc_source {
+ (None, Some(path.first().unwrap().short_channel_id), true, Some(*failure_code), Some(data.clone()))
+ } else { unreachable!(); }
+ }
+ }
+ }
+}
+
/// Allows `decode_next_hop` to return the next hop packet bytes for either payments or onion
/// message forwards.
pub(crate) trait NextPacketBytes: AsMut<[u8]> {
use crate::ln::msgs;
use crate::ln::msgs::ChannelMessageHandler;
use crate::routing::gossip::RoutingFees;
-use crate::routing::router::{find_route, get_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
+use crate::routing::router::{get_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::util::test_utils;
use crate::util::errors::APIError;
final_value_msat: amt_msat,
final_cltv_expiry_delta: TEST_FINAL_CLTV,
};
- let route = find_route(
- &nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph, None, nodes[0].logger,
- &scorer, &random_seed_bytes
+ let route = get_route(
+ &nodes[0].node.get_our_node_id(), &route_params.payment_params,
+ &nodes[0].network_graph.read_only(), None, route_params.final_value_msat,
+ route_params.final_cltv_expiry_delta, nodes[0].logger, &scorer, &random_seed_bytes
).unwrap();
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60).unwrap();
// Check for unknown channel id error.
let unknown_chan_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &[42; 32], nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
- assert_eq!(unknown_chan_id_err , APIError::APIMisuseError { err: format!("Channel with id {:?} not found", [42; 32]) });
+ assert_eq!(unknown_chan_id_err , APIError::ChannelUnavailable { err: format!("Channel with id {} not found", log_bytes!([42; 32])) });
if test == InterceptTest::Fail {
// Ensure we can fail the intercepted payment back.
// Check that we'll fail as expected when sending to a channel that isn't in `ChannelReady` yet.
let temp_chan_id = nodes[1].node.create_channel(nodes[2].node.get_our_node_id(), 100_000, 0, 42, None).unwrap();
let unusable_chan_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &temp_chan_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
- assert_eq!(unusable_chan_err , APIError::APIMisuseError { err: format!("Channel with id {:?} not fully established", temp_chan_id) });
+ assert_eq!(unusable_chan_err , APIError::ChannelUnavailable { err: format!("Channel with id {} not fully established", log_bytes!(temp_chan_id)) });
assert_eq!(nodes[1].node.get_and_clear_pending_msg_events().len(), 1);
// Open the just-in-time channel so the payment can then be forwarded.
expect_pending_htlcs_forwardable!(nodes[2]);
let payment_preimage = nodes[2].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
- expect_payment_received!(&nodes[2], payment_hash, payment_secret, amt_msat, Some(payment_preimage), nodes[2].node.get_our_node_id());
+ expect_payment_claimable!(&nodes[2], payment_hash, payment_secret, amt_msat, Some(payment_preimage), nodes[2].node.get_our_node_id());
do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1], &nodes[2])[..]), false, payment_preimage);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
};
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
- let block_count = 183; // find_route adds a random CLTV offset, so hardcode rather than summing consts
- for _ in 0..block_count {
+ for _ in 0..TEST_FINAL_CLTV {
block.header.prev_blockhash = block.block_hash();
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
// Check for unknown intercept id error.
let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
- assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", intercept_id.0) });
+ assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0)) });
+ let unknown_intercept_id_err = nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap_err();
+ assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0)) });
}
}
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &bs_send.msgs[0]);
commitment_signed_dance!(nodes[2], nodes[1], bs_send.commitment_msg, false);
expect_pending_htlcs_forwardable!(nodes[2]);
- expect_payment_received!(nodes[2], payment_hash, payment_secret, 1_000_000);
+ expect_payment_claimable!(nodes[2], payment_hash, payment_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
confirm_transaction(&nodes[0], &tx);
//! Functional tests which test for correct behavior across node restarts.
use crate::chain::{ChannelMonitorUpdateStatus, Watch};
+use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::channelmonitor::ChannelMonitor;
+use crate::chain::keysinterface::KeysInterface;
use crate::chain::transaction::OutPoint;
use crate::ln::channelmanager::{self, ChannelManager, ChannelManagerReadArgs, PaymentId};
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::enforcing_trait_impls::EnforcingSigner;
use crate::util::test_utils;
-use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
+use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::config::UserConfig;
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
expect_pending_htlcs_forwardable!(nodes[1]);
- expect_payment_received!(nodes[1], payment_hash, payment_secret, 100_000);
+ expect_payment_claimable!(nodes[1], payment_hash, payment_secret, 100_000);
check_added_monitors!(nodes[1], 1);
let mut events = nodes[1].node.get_and_clear_pending_msg_events();
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event.msgs[0]);
commitment_signed_dance!(nodes[2], nodes[1], payment_event.commitment_msg, false);
expect_pending_htlcs_forwardable!(nodes[2]);
- expect_payment_received!(nodes[2], payment_hash_2, payment_secret_2, 200_000);
+ expect_payment_claimable!(nodes[2], payment_hash_2, payment_secret_2, 200_000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage_2);
// Test what happens if a node receives an MPP payment, claims it, but crashes before
// persisting the ChannelManager. If `persist_both_monitors` is false, also crash after only
// updating one of the two channels' ChannelMonitors. As a result, on startup, we'll (a) still
- // have the PaymentReceived event, (b) have one (or two) channel(s) that goes on chain with the
+ // have the PaymentClaimable event, (b) have one (or two) channel(s) that goes on chain with the
// HTLC preimage in them, and (c) optionally have one channel that is live off-chain but does
// not have the preimage tied to the still-pending HTLC.
//
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 2);
- // Send the payment through to nodes[3] *without* clearing the PaymentReceived event
+ // Send the payment through to nodes[3] *without* clearing the PaymentClaimable event
let mut send_events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(send_events.len(), 2);
do_pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 15_000_000, payment_hash, Some(payment_secret), send_events[0].clone(), true, false, None);
let original_manager = nodes[3].node.encode();
- expect_payment_received!(nodes[3], payment_hash, payment_secret, 15_000_000);
+ expect_payment_claimable!(nodes[3], payment_hash, payment_secret, 15_000_000);
nodes[3].node.claim_funds(payment_preimage);
check_added_monitors!(nodes[3], 2);
nodes[2].node.peer_disconnected(&nodes[3].node.get_our_node_id(), false);
// During deserialization, we should have closed one channel and broadcast its latest
- // commitment transaction. We should also still have the original PaymentReceived event we
+ // commitment transaction. We should also still have the original PaymentClaimable event we
// never finished processing.
let events = nodes[3].node.get_and_clear_pending_events();
assert_eq!(events.len(), if persist_both_monitors { 4 } else { 3 });
- if let Event::PaymentReceived { amount_msat: 15_000_000, .. } = events[0] { } else { panic!(); }
+ if let Event::PaymentClaimable { amount_msat: 15_000_000, .. } = events[0] { } else { panic!(); }
if let Event::ChannelClosed { reason: ClosureReason::OutdatedChannelManager, .. } = events[1] { } else { panic!(); }
if persist_both_monitors {
if let Event::ChannelClosed { reason: ClosureReason::OutdatedChannelManager, .. } = events[2] { } else { panic!(); }
do_test_partial_claim_before_restart(false);
do_test_partial_claim_before_restart(true);
}
+
+fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_htlc: bool) {
+ if !use_cs_commitment { assert!(!claim_htlc); }
+ // If we go to forward a payment, and the ChannelMonitor persistence completes, but the
+ // ChannelManager does not, we shouldn't try to forward the payment again, nor should we fail
+ // it back until the ChannelMonitor decides the fate of the HTLC.
+ // This was never an issue, but it may be easy to regress here going forward.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+
+ let persister;
+ let new_chain_monitor;
+ let nodes_1_deserialized;
+
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let chan_id_1 = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+ let chan_id_2 = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+
+ let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
+ let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes());
+ let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV;
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
+ check_added_monitors!(nodes[0], 1);
+
+ let payment_event = SendEvent::from_node(&nodes[0]);
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
+
+ let node_encoded = nodes[1].node.encode();
+
+ expect_pending_htlcs_forwardable!(nodes[1]);
+
+ let payment_event = SendEvent::from_node(&nodes[1]);
+ nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event.msgs[0]);
+ nodes[2].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &payment_event.commitment_msg);
+ check_added_monitors!(nodes[2], 1);
+
+ if claim_htlc {
+ get_monitor!(nodes[2], chan_id_2).provide_payment_preimage(&payment_hash, &payment_preimage,
+ &nodes[2].tx_broadcaster, &LowerBoundedFeeEstimator(nodes[2].fee_estimator), &nodes[2].logger);
+ }
+ assert!(nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
+
+ let _ = nodes[2].node.get_and_clear_pending_msg_events();
+
+ nodes[2].node.force_close_broadcasting_latest_txn(&chan_id_2, &nodes[1].node.get_our_node_id()).unwrap();
+ let cs_commitment_tx = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(cs_commitment_tx.len(), if claim_htlc { 2 } else { 1 });
+
+ check_added_monitors!(nodes[2], 1);
+ check_closed_event!(nodes[2], 1, ClosureReason::HolderForceClosed);
+ check_closed_broadcast!(nodes[2], true);
+
+ let chan_0_monitor_serialized = get_monitor!(nodes[1], chan_id_1).encode();
+ let chan_1_monitor_serialized = get_monitor!(nodes[1], chan_id_2).encode();
+ reload_node!(nodes[1], node_encoded, &[&chan_0_monitor_serialized, &chan_1_monitor_serialized], persister, new_chain_monitor, nodes_1_deserialized);
+
+ check_closed_event!(nodes[1], 1, ClosureReason::OutdatedChannelManager);
+
+ let bs_commitment_tx = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(bs_commitment_tx.len(), 1);
+
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), true);
+ reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
+ if use_cs_commitment {
+ // If we confirm a commitment transaction that has the HTLC on-chain, nodes[1] should wait
+ // for an HTLC-spending transaction before it does anything with the HTLC upstream.
+ confirm_transaction(&nodes[1], &cs_commitment_tx[0]);
+ assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+
+ if claim_htlc {
+ confirm_transaction(&nodes[1], &cs_commitment_tx[1]);
+ } else {
+ connect_blocks(&nodes[1], htlc_expiry - nodes[1].best_block_info().1);
+ let bs_htlc_timeout_tx = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(bs_htlc_timeout_tx.len(), 1);
+ confirm_transaction(&nodes[1], &bs_htlc_timeout_tx[0]);
+ }
+ } else {
+ confirm_transaction(&nodes[1], &bs_commitment_tx[0]);
+ }
+
+ if !claim_htlc {
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_id_2 }]);
+ } else {
+ expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, true);
+ }
+ check_added_monitors!(nodes[1], 1);
+
+ let events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::UpdateHTLCs { updates: msgs::CommitmentUpdate { update_fulfill_htlcs, update_fail_htlcs, commitment_signed, .. }, .. } => {
+ if claim_htlc {
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &update_fulfill_htlcs[0]);
+ } else {
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &update_fail_htlcs[0]);
+ }
+ commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false);
+ },
+ _ => panic!("Unexpected event"),
+ }
+
+ if claim_htlc {
+ expect_payment_sent!(nodes[0], payment_preimage);
+ } else {
+ expect_payment_failed!(nodes[0], payment_hash, false);
+ }
+}
+
+#[test]
+fn forwarded_payment_no_manager_persistence() {
+ do_forwarded_payment_no_manager_persistence(true, true);
+ do_forwarded_payment_no_manager_persistence(true, false);
+ do_forwarded_payment_no_manager_persistence(false, false);
+}
+
+#[test]
+fn removed_payment_no_manager_persistence() {
+ // If an HTLC is failed to us on a channel, and the ChannelMonitor persistence completes, but
+ // the corresponding ChannelManager persistence does not, we need to ensure that the HTLC is
+ // still failed back to the previous hop even though the ChannelMonitor now no longer is aware
+ // of the HTLC. This was previously broken as no attempt was made to figure out which HTLCs
+ // were left dangling when a channel was force-closed due to a stale ChannelManager.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+
+ let persister;
+ let new_chain_monitor;
+ let nodes_1_deserialized;
+
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let chan_id_1 = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+ let chan_id_2 = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+
+ let (_, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000);
+
+ let node_encoded = nodes[1].node.encode();
+
+ nodes[2].node.fail_htlc_backwards(&payment_hash);
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[2], [HTLCDestination::FailedPayment { payment_hash }]);
+ check_added_monitors!(nodes[2], 1);
+ let events = nodes[2].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::UpdateHTLCs { updates: msgs::CommitmentUpdate { update_fail_htlcs, commitment_signed, .. }, .. } => {
+ nodes[1].node.handle_update_fail_htlc(&nodes[2].node.get_our_node_id(), &update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[1], nodes[2], commitment_signed, false);
+ },
+ _ => panic!("Unexpected event"),
+ }
+
+ let chan_0_monitor_serialized = get_monitor!(nodes[1], chan_id_1).encode();
+ let chan_1_monitor_serialized = get_monitor!(nodes[1], chan_id_2).encode();
+ reload_node!(nodes[1], node_encoded, &[&chan_0_monitor_serialized, &chan_1_monitor_serialized], persister, new_chain_monitor, nodes_1_deserialized);
+
+ match nodes[1].node.pop_pending_event().unwrap() {
+ Event::ChannelClosed { ref reason, .. } => {
+ assert_eq!(*reason, ClosureReason::OutdatedChannelManager);
+ },
+ _ => panic!("Unexpected event"),
+ }
+
+ // Now that the ChannelManager has force-closed the channel which had the HTLC removed, it is
+ // now forgotten everywhere. The ChannelManager should have, as a side-effect of reload,
+ // learned that the HTLC is gone from the ChannelMonitor and added it to the to-fail-back set.
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), true);
+ reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_id_2 }]);
+ check_added_monitors!(nodes[1], 1);
+ let events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::UpdateHTLCs { updates: msgs::CommitmentUpdate { update_fail_htlcs, commitment_signed, .. }, .. } => {
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false);
+ },
+ _ => panic!("Unexpected event"),
+ }
+
+ expect_payment_failed!(nodes[0], payment_hash, false);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Data structures and encoding for `invoice_request` messages.
+//!
+//! An [`InvoiceRequest`] can be either built from a parsed [`Offer`] as an "offer to be paid" or
+//! built directly as an "offer for money" (e.g., refund, ATM withdrawal). In the former case, it is
+//! typically constructed by a customer and sent to the merchant who had published the corresponding
+//! offer. In the latter case, an offer doesn't exist as a precursor to the request. Rather the
+//! merchant would typically construct the invoice request and present it to the customer.
+//!
+//! The recipient of the request responds with an `Invoice`.
+//!
+//! ```ignore
+//! extern crate bitcoin;
+//! extern crate lightning;
+//!
+//! use bitcoin::network::constants::Network;
+//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use core::convert::Infallible;
+//! use lightning::ln::features::OfferFeatures;
+//! use lightning::offers::offer::Offer;
+//! use lightning::util::ser::Writeable;
+//!
+//! # fn parse() -> Result<(), lightning::offers::parse::ParseError> {
+//! let secp_ctx = Secp256k1::new();
+//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
+//! let pubkey = PublicKey::from(keys);
+//! let mut buffer = Vec::new();
+//!
+//! // "offer to be paid" flow
+//! "lno1qcp4256ypq"
+//! .parse::<Offer>()?
+//! .request_invoice(vec![42; 64], pubkey)?
+//! .chain(Network::Testnet)?
+//! .amount_msats(1000)?
+//! .quantity(5)?
+//! .payer_note("foo".to_string())
+//! .build()?
+//! .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys)))
+//! .expect("failed verifying signature")
+//! .write(&mut buffer)
+//! .unwrap();
+//! # Ok(())
+//! # }
+//! ```
+
+use bitcoin::blockdata::constants::ChainHash;
+use bitcoin::network::constants::Network;
+use bitcoin::secp256k1::{Message, PublicKey};
+use bitcoin::secp256k1::schnorr::Signature;
+use core::convert::TryFrom;
+use crate::io;
+use crate::ln::features::InvoiceRequestFeatures;
+use crate::ln::msgs::DecodeError;
+use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self};
+use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::parse::{ParseError, ParsedMessage, SemanticError};
+use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
+use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
+use crate::util::string::PrintableString;
+
+use crate::prelude::*;
+
+const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature");
+
+/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [module-level documentation]: self
+pub struct InvoiceRequestBuilder<'a> {
+ offer: &'a Offer,
+ invoice_request: InvoiceRequestContents,
+}
+
+impl<'a> InvoiceRequestBuilder<'a> {
+ pub(super) fn new(offer: &'a Offer, metadata: Vec<u8>, payer_id: PublicKey) -> Self {
+ Self {
+ offer,
+ invoice_request: InvoiceRequestContents {
+ payer: PayerContents(metadata), offer: offer.contents.clone(), chain: None,
+ amount_msats: None, features: InvoiceRequestFeatures::empty(), quantity: None,
+ payer_id, payer_note: None,
+ },
+ }
+ }
+
+ /// Sets the [`InvoiceRequest::chain`] of the given [`Network`] for paying an invoice. If not
+ /// called, [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported
+ /// by the offer.
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub fn chain(mut self, network: Network) -> Result<Self, SemanticError> {
+ let chain = ChainHash::using_genesis_block(network);
+ if !self.offer.supports_chain(chain) {
+ return Err(SemanticError::UnsupportedChain);
+ }
+
+ self.invoice_request.chain = Some(chain);
+ Ok(self)
+ }
+
+ /// Sets the [`InvoiceRequest::amount_msats`] for paying an invoice. Errors if `amount_msats` is
+ /// not at least the expected invoice amount (i.e., [`Offer::amount`] times [`quantity`]).
+ ///
+ /// Successive calls to this method will override the previous setting.
+ ///
+ /// [`quantity`]: Self::quantity
+ pub fn amount_msats(mut self, amount_msats: u64) -> Result<Self, SemanticError> {
+ self.invoice_request.offer.check_amount_msats_for_quantity(
+ Some(amount_msats), self.invoice_request.quantity
+ )?;
+ self.invoice_request.amount_msats = Some(amount_msats);
+ Ok(self)
+ }
+
+ /// Sets [`InvoiceRequest::quantity`] of items. If not set, `1` is assumed. Errors if `quantity`
+ /// does not conform to [`Offer::is_valid_quantity`].
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub fn quantity(mut self, quantity: u64) -> Result<Self, SemanticError> {
+ self.invoice_request.offer.check_quantity(Some(quantity))?;
+ self.invoice_request.quantity = Some(quantity);
+ Ok(self)
+ }
+
+ /// Sets the [`InvoiceRequest::payer_note`].
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub fn payer_note(mut self, payer_note: String) -> Self {
+ self.invoice_request.payer_note = Some(payer_note);
+ self
+ }
+
+ /// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
+ /// by [`UnsignedInvoiceRequest::sign`].
+ pub fn build(mut self) -> Result<UnsignedInvoiceRequest<'a>, SemanticError> {
+ #[cfg(feature = "std")] {
+ if self.offer.is_expired() {
+ return Err(SemanticError::AlreadyExpired);
+ }
+ }
+
+ let chain = self.invoice_request.chain();
+ if !self.offer.supports_chain(chain) {
+ return Err(SemanticError::UnsupportedChain);
+ }
+
+ if chain == self.offer.implied_chain() {
+ self.invoice_request.chain = None;
+ }
+
+ if self.offer.amount().is_none() && self.invoice_request.amount_msats.is_none() {
+ return Err(SemanticError::MissingAmount);
+ }
+
+ self.invoice_request.offer.check_quantity(self.invoice_request.quantity)?;
+ self.invoice_request.offer.check_amount_msats_for_quantity(
+ self.invoice_request.amount_msats, self.invoice_request.quantity
+ )?;
+
+ let InvoiceRequestBuilder { offer, invoice_request } = self;
+ Ok(UnsignedInvoiceRequest { offer, invoice_request })
+ }
+}
+
+#[cfg(test)]
+impl<'a> InvoiceRequestBuilder<'a> {
+ fn chain_unchecked(mut self, network: Network) -> Self {
+ let chain = ChainHash::using_genesis_block(network);
+ self.invoice_request.chain = Some(chain);
+ self
+ }
+
+ fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
+ self.invoice_request.amount_msats = Some(amount_msats);
+ self
+ }
+
+ fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
+ self.invoice_request.features = features;
+ self
+ }
+
+ fn quantity_unchecked(mut self, quantity: u64) -> Self {
+ self.invoice_request.quantity = Some(quantity);
+ self
+ }
+
+ pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> {
+ let InvoiceRequestBuilder { offer, invoice_request } = self;
+ UnsignedInvoiceRequest { offer, invoice_request }
+ }
+}
+
+/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
+pub struct UnsignedInvoiceRequest<'a> {
+ offer: &'a Offer,
+ invoice_request: InvoiceRequestContents,
+}
+
+impl<'a> UnsignedInvoiceRequest<'a> {
+ /// Signs the invoice request using the given function.
+ pub fn sign<F, E>(self, sign: F) -> Result<InvoiceRequest, SignError<E>>
+ where
+ F: FnOnce(&Message) -> Result<Signature, E>
+ {
+ // 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);
+ 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 = Some(merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?);
+
+ // Append the signature TLV record to the bytes.
+ let signature_tlv_stream = SignatureTlvStreamRef {
+ signature: signature.as_ref(),
+ };
+ signature_tlv_stream.write(&mut bytes).unwrap();
+
+ Ok(InvoiceRequest {
+ bytes,
+ contents: self.invoice_request,
+ signature,
+ })
+ }
+}
+
+/// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
+///
+/// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request
+/// specifies these such that its recipient can send an invoice for payment.
+///
+/// [`Offer`]: crate::offers::offer::Offer
+#[derive(Clone, Debug)]
+pub struct InvoiceRequest {
+ pub(super) bytes: Vec<u8>,
+ contents: InvoiceRequestContents,
+ signature: Option<Signature>,
+}
+
+/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
+#[derive(Clone, Debug)]
+pub(super) struct InvoiceRequestContents {
+ payer: PayerContents,
+ offer: OfferContents,
+ chain: Option<ChainHash>,
+ amount_msats: Option<u64>,
+ features: InvoiceRequestFeatures,
+ quantity: Option<u64>,
+ payer_id: PublicKey,
+ payer_note: Option<String>,
+}
+
+impl InvoiceRequest {
+ /// 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.payer.0[..]
+ }
+
+ /// A chain from [`Offer::chains`] that the offer is valid for.
+ pub fn chain(&self) -> ChainHash {
+ self.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.amount_msats
+ }
+
+ /// Features for paying the invoice.
+ pub fn features(&self) -> &InvoiceRequestFeatures {
+ &self.contents.features
+ }
+
+ /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
+ pub fn quantity(&self) -> Option<u64> {
+ self.contents.quantity
+ }
+
+ /// A possibly transient pubkey used to sign the invoice request.
+ pub fn payer_id(&self) -> PublicKey {
+ self.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.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
+ }
+
+ /// Signature of the invoice request using [`payer_id`].
+ ///
+ /// [`payer_id`]: Self::payer_id
+ pub fn signature(&self) -> Option<Signature> {
+ self.signature
+ }
+
+ #[cfg(test)]
+ fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
+ let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
+ self.contents.as_tlv_stream();
+ let signature_tlv_stream = SignatureTlvStreamRef {
+ signature: self.signature.as_ref(),
+ };
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
+ }
+}
+
+impl InvoiceRequestContents {
+ fn chain(&self) -> ChainHash {
+ self.chain.unwrap_or_else(|| self.offer.implied_chain())
+ }
+
+ pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
+ let payer = PayerTlvStreamRef {
+ metadata: Some(&self.payer.0),
+ };
+
+ let offer = self.offer.as_tlv_stream();
+
+ let features = {
+ if self.features == InvoiceRequestFeatures::empty() { None }
+ else { Some(&self.features) }
+ };
+
+ let invoice_request = InvoiceRequestTlvStreamRef {
+ chain: self.chain.as_ref(),
+ amount: self.amount_msats,
+ features,
+ quantity: self.quantity,
+ payer_id: Some(&self.payer_id),
+ payer_note: self.payer_note.as_ref(),
+ };
+
+ (payer, offer, invoice_request)
+ }
+}
+
+impl Writeable for InvoiceRequest {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ WithoutLength(&self.bytes).write(writer)
+ }
+}
+
+impl Writeable for InvoiceRequestContents {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ self.as_tlv_stream().write(writer)
+ }
+}
+
+tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
+ (80, chain: ChainHash),
+ (82, amount: (u64, HighZeroBytesDroppedBigSize)),
+ (84, features: InvoiceRequestFeatures),
+ (86, quantity: (u64, HighZeroBytesDroppedBigSize)),
+ (88, payer_id: PublicKey),
+ (89, payer_note: (String, WithoutLength)),
+});
+
+type FullInvoiceRequestTlvStream =
+ (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
+
+#[cfg(test)]
+type FullInvoiceRequestTlvStreamRef<'a> = (
+ PayerTlvStreamRef<'a>,
+ OfferTlvStreamRef<'a>,
+ InvoiceRequestTlvStreamRef<'a>,
+ SignatureTlvStreamRef<'a>,
+);
+
+impl SeekReadable for FullInvoiceRequestTlvStream {
+ 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 signature = SeekReadable::read(r)?;
+
+ Ok((payer, offer, invoice_request, signature))
+ }
+}
+
+type PartialInvoiceRequestTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
+
+type PartialInvoiceRequestTlvStreamRef<'a> = (
+ PayerTlvStreamRef<'a>,
+ OfferTlvStreamRef<'a>,
+ InvoiceRequestTlvStreamRef<'a>,
+);
+
+impl TryFrom<Vec<u8>> for InvoiceRequest {
+ type Error = ParseError;
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ let invoice_request = ParsedMessage::<FullInvoiceRequestTlvStream>::try_from(bytes)?;
+ let ParsedMessage { bytes, tlv_stream } = invoice_request;
+ let (
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+ SignatureTlvStream { signature },
+ ) = tlv_stream;
+ let contents = InvoiceRequestContents::try_from(
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+ )?;
+
+ if let Some(signature) = &signature {
+ merkle::verify_signature(signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
+ }
+
+ Ok(InvoiceRequest { bytes, contents, signature })
+ }
+}
+
+impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
+ type Error = SemanticError;
+
+ fn try_from(tlv_stream: PartialInvoiceRequestTlvStream) -> Result<Self, Self::Error> {
+ let (
+ PayerTlvStream { metadata },
+ offer_tlv_stream,
+ InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
+ ) = tlv_stream;
+
+ let payer = match metadata {
+ None => return Err(SemanticError::MissingPayerMetadata),
+ Some(metadata) => PayerContents(metadata),
+ };
+ let offer = OfferContents::try_from(offer_tlv_stream)?;
+
+ if !offer.supports_chain(chain.unwrap_or_else(|| offer.implied_chain())) {
+ return Err(SemanticError::UnsupportedChain);
+ }
+
+ if offer.amount().is_none() && amount.is_none() {
+ return Err(SemanticError::MissingAmount);
+ }
+
+ offer.check_quantity(quantity)?;
+ offer.check_amount_msats_for_quantity(amount, quantity)?;
+
+ let features = features.unwrap_or_else(InvoiceRequestFeatures::empty);
+
+ let payer_id = match payer_id {
+ None => return Err(SemanticError::MissingPayerId),
+ Some(payer_id) => payer_id,
+ };
+
+ Ok(InvoiceRequestContents {
+ payer, offer, chain, amount_msats: amount, features, quantity, payer_id, payer_note,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::InvoiceRequest;
+
+ use bitcoin::blockdata::constants::ChainHash;
+ use bitcoin::network::constants::Network;
+ use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, SecretKey, self};
+ use bitcoin::secp256k1::schnorr::Signature;
+ use core::convert::{Infallible, TryFrom};
+ use core::num::NonZeroU64;
+ #[cfg(feature = "std")]
+ use core::time::Duration;
+ use crate::ln::features::InvoiceRequestFeatures;
+ use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+ use crate::offers::merkle::SignError;
+ use crate::offers::offer::{Amount, OfferBuilder, Quantity};
+ use crate::offers::parse::{ParseError, SemanticError};
+ use crate::util::ser::{BigSize, Writeable};
+ use crate::util::string::PrintableString;
+
+ fn payer_keys() -> KeyPair {
+ let secp_ctx = Secp256k1::new();
+ KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
+ }
+
+ fn payer_sign(digest: &Message) -> 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))
+ }
+
+ fn payer_pubkey() -> PublicKey {
+ payer_keys().public_key()
+ }
+
+ fn recipient_sign(digest: &Message) -> 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))
+ }
+
+ fn recipient_pubkey() -> PublicKey {
+ let secp_ctx = Secp256k1::new();
+ KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap()).public_key()
+ }
+
+ #[test]
+ fn builds_invoice_request_with_defaults() {
+ let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap();
+ let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap().sign(payer_sign).unwrap();
+
+ let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream) =
+ invoice_request.as_tlv_stream();
+ 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.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.quantity(), None);
+ assert_eq!(invoice_request.payer_id(), payer_pubkey());
+ assert_eq!(invoice_request.payer_note(), None);
+ assert!(invoice_request.signature().is_some());
+
+ assert_eq!(payer_tlv_stream.metadata, Some(&vec![1; 32]));
+ assert_eq!(offer_tlv_stream.chains, None);
+ assert_eq!(offer_tlv_stream.metadata, None);
+ assert_eq!(offer_tlv_stream.currency, None);
+ assert_eq!(offer_tlv_stream.amount, Some(1000));
+ assert_eq!(offer_tlv_stream.description, Some(&String::from("foo")));
+ assert_eq!(offer_tlv_stream.features, None);
+ assert_eq!(offer_tlv_stream.absolute_expiry, None);
+ assert_eq!(offer_tlv_stream.paths, None);
+ assert_eq!(offer_tlv_stream.issuer, None);
+ assert_eq!(offer_tlv_stream.quantity_max, None);
+ assert_eq!(offer_tlv_stream.node_id, Some(&recipient_pubkey()));
+ assert_eq!(invoice_request_tlv_stream.chain, None);
+ assert_eq!(invoice_request_tlv_stream.amount, None);
+ assert_eq!(invoice_request_tlv_stream.features, None);
+ assert_eq!(invoice_request_tlv_stream.quantity, None);
+ assert_eq!(invoice_request_tlv_stream.payer_id, Some(&payer_pubkey()));
+ assert_eq!(invoice_request_tlv_stream.payer_note, None);
+ assert!(signature_tlv_stream.signature.is_some());
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice request: {:?}", e);
+ }
+ }
+
+ #[cfg(feature = "std")]
+ #[test]
+ fn builds_invoice_request_from_offer_with_expiration() {
+ let future_expiry = Duration::from_secs(u64::max_value());
+ let past_expiry = Duration::from_secs(0);
+
+ if let Err(e) = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .absolute_expiry(future_expiry)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build()
+ {
+ panic!("error building invoice_request: {:?}", e);
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .absolute_expiry(past_expiry)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build()
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::AlreadyExpired),
+ }
+ }
+
+ #[test]
+ fn builds_invoice_request_with_chain() {
+ let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
+ let testnet = ChainHash::using_genesis_block(Network::Testnet);
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Bitcoin).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.chain(), mainnet);
+ assert_eq!(tlv_stream.chain, None);
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .chain(Network::Testnet)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Testnet).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.chain(), testnet);
+ assert_eq!(tlv_stream.chain, Some(&testnet));
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .chain(Network::Bitcoin)
+ .chain(Network::Testnet)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Bitcoin).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.chain(), mainnet);
+ assert_eq!(tlv_stream.chain, None);
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .chain(Network::Bitcoin)
+ .chain(Network::Testnet)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Bitcoin).unwrap()
+ .chain(Network::Testnet).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.chain(), testnet);
+ assert_eq!(tlv_stream.chain, Some(&testnet));
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .chain(Network::Testnet)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Bitcoin)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::UnsupportedChain),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .chain(Network::Testnet)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build()
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::UnsupportedChain),
+ }
+ }
+
+ #[test]
+ fn builds_invoice_request_with_amount() {
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(1000).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.amount_msats(), Some(1000));
+ assert_eq!(tlv_stream.amount, Some(1000));
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(1001).unwrap()
+ .amount_msats(1000).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.amount_msats(), Some(1000));
+ assert_eq!(tlv_stream.amount, Some(1000));
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(1001).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.amount_msats(), Some(1001));
+ assert_eq!(tlv_stream.amount, Some(1001));
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(999)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .quantity(2).unwrap()
+ .amount_msats(1000)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(MAX_VALUE_MSAT + 1)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::InvalidAmount),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(1000).unwrap()
+ .quantity(2).unwrap()
+ .build()
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::InsufficientAmount),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build()
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::MissingAmount),
+ }
+ }
+
+ #[test]
+ fn builds_invoice_request_with_features() {
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .features_unchecked(InvoiceRequestFeatures::unknown())
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::unknown());
+ assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown()));
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .features_unchecked(InvoiceRequestFeatures::unknown())
+ .features_unchecked(InvoiceRequestFeatures::empty())
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(tlv_stream.features, None);
+ }
+
+ #[test]
+ fn builds_invoice_request_with_quantity() {
+ let ten = NonZeroU64::new(10).unwrap();
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::one())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.quantity(), None);
+ assert_eq!(tlv_stream.quantity, None);
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::one())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(2_000).unwrap()
+ .quantity(2)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::UnexpectedQuantity),
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Bounded(ten))
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(10_000).unwrap()
+ .quantity(10).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.amount_msats(), Some(10_000));
+ assert_eq!(tlv_stream.amount, Some(10_000));
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Bounded(ten))
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(11_000).unwrap()
+ .quantity(11)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::InvalidQuantity),
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(2_000).unwrap()
+ .quantity(2).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.amount_msats(), Some(2_000));
+ assert_eq!(tlv_stream.amount, Some(2_000));
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build()
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::MissingQuantity),
+ }
+ }
+
+ #[test]
+ fn builds_invoice_request_with_payer_note() {
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .payer_note("bar".into())
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.payer_note(), Some(PrintableString("bar")));
+ assert_eq!(tlv_stream.payer_note, Some(&String::from("bar")));
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .payer_note("bar".into())
+ .payer_note("baz".into())
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+ let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
+ assert_eq!(invoice_request.payer_note(), Some(PrintableString("baz")));
+ assert_eq!(tlv_stream.payer_note, Some(&String::from("baz")));
+ }
+
+ #[test]
+ fn fails_signing_invoice_request() {
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(|_| Err(()))
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SignError::Signing(())),
+ }
+
+ match OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(recipient_sign)
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SignError::Verification(secp256k1::Error::InvalidSignature)),
+ }
+ }
+
+ #[test]
+ fn parses_invoice_request_with_metadata() {
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![42; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+ }
+
+ #[test]
+ fn parses_invoice_request_with_chain() {
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain(Network::Bitcoin).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .chain_unchecked(Network::Testnet)
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedChain)),
+ }
+ }
+
+ #[test]
+ fn parses_invoice_request_with_amount() {
+ let 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();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(1000).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingAmount)),
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats_unchecked(999)
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InsufficientAmount)),
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount(Amount::Currency { iso4217_code: *b"USD", amount: 1000 })
+ .build_unchecked()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedCurrency));
+ },
+ }
+ }
+
+ #[test]
+ fn parses_invoice_request_with_quantity() {
+ let ten = NonZeroU64::new(10).unwrap();
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::one())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::one())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(2_000).unwrap()
+ .quantity_unchecked(2)
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnexpectedQuantity));
+ },
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Bounded(ten))
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(10_000).unwrap()
+ .quantity(10).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Bounded(ten))
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(11_000).unwrap()
+ .quantity_unchecked(11)
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidQuantity)),
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .amount_msats(2_000).unwrap()
+ .quantity(2).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .supported_quantity(Quantity::Unbounded)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build_unchecked()
+ .sign(payer_sign).unwrap();
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingQuantity)),
+ }
+ }
+
+ #[test]
+ fn fails_parsing_invoice_request_without_metadata() {
+ let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .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();
+ tlv_stream.0.metadata = None;
+
+ let mut buffer = Vec::new();
+ tlv_stream.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerMetadata));
+ },
+ }
+ }
+
+ #[test]
+ fn fails_parsing_invoice_request_without_payer_id() {
+ let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .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();
+ tlv_stream.2.payer_id = None;
+
+ let mut buffer = Vec::new();
+ tlv_stream.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerId)),
+ }
+ }
+
+ #[test]
+ fn fails_parsing_invoice_request_without_node_id() {
+ let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .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();
+ tlv_stream.1.node_id = None;
+
+ let mut buffer = Vec::new();
+ tlv_stream.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));
+ },
+ }
+ }
+
+ #[test]
+ fn parses_invoice_request_without_signature() {
+ let mut buffer = Vec::new();
+ OfferBuilder::new("foo".into(), recipient_pubkey())
+ .amount_msats(1000)
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .invoice_request
+ .write(&mut buffer).unwrap();
+
+ if let Err(e) = InvoiceRequest::try_from(buffer) {
+ panic!("error parsing invoice_request: {:?}", e);
+ }
+ }
+
+ #[test]
+ fn fails_parsing_invoice_request_with_invalid_signature() {
+ let mut 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();
+ let last_signature_byte = invoice_request.bytes.last_mut().unwrap();
+ *last_signature_byte = last_signature_byte.wrapping_add(1);
+
+ let mut buffer = Vec::new();
+ invoice_request.write(&mut buffer).unwrap();
+
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSignature(secp256k1::Error::InvalidSignature));
+ },
+ }
+ }
+
+ #[test]
+ fn fails_parsing_invoice_request_with_extra_tlv_records() {
+ let secp_ctx = Secp256k1::new();
+ let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let invoice_request = OfferBuilder::new("foo".into(), keys.public_key())
+ .amount_msats(1000)
+ .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)))
+ .unwrap();
+
+ let mut encoded_invoice_request = Vec::new();
+ invoice_request.write(&mut encoded_invoice_request).unwrap();
+ BigSize(1002).write(&mut encoded_invoice_request).unwrap();
+ BigSize(32).write(&mut encoded_invoice_request).unwrap();
+ [42u8; 32].write(&mut encoded_invoice_request).unwrap();
+
+ match InvoiceRequest::try_from(encoded_invoice_request) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::Decode(DecodeError::InvalidValue)),
+ }
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Tagged hashes for use in signature calculation and verification.
+
+use bitcoin::hashes::{Hash, HashEngine, sha256};
+use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, self};
+use bitcoin::secp256k1::schnorr::Signature;
+use crate::io;
+use crate::util::ser::{BigSize, Readable};
+
+use crate::prelude::*;
+
+/// Valid type range for signature TLV records.
+const SIGNATURE_TYPES: core::ops::RangeInclusive<u64> = 240..=1000;
+
+tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
+ (240, signature: Signature),
+});
+
+/// Error when signing messages.
+#[derive(Debug, PartialEq)]
+pub enum SignError<E> {
+ /// User-defined error when signing the message.
+ Signing(E),
+ /// Error when verifying the produced signature using the given pubkey.
+ 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.
+///
+/// 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,
+) -> Result<Signature, SignError<E>>
+where
+ F: FnOnce(&Message) -> Result<Signature, E>
+{
+ let digest = message_digest(tag, bytes);
+ let signature = sign(&digest).map_err(|e| SignError::Signing(e))?;
+
+ let pubkey = pubkey.into();
+ let secp_ctx = Secp256k1::verification_only();
+ 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
+/// 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,
+) -> Result<(), secp256k1::Error> {
+ let digest = message_digest(tag, bytes);
+ let pubkey = pubkey.into();
+ let secp_ctx = Secp256k1::verification_only();
+ secp_ctx.verify_schnorr(signature, &digest, &pubkey)
+}
+
+fn message_digest(tag: &str, bytes: &[u8]) -> Message {
+ let tag = sha256::Hash::hash(tag.as_bytes());
+ let merkle_root = root_hash(bytes);
+ Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()
+}
+
+/// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
+/// containing at least one TLV record.
+fn root_hash(data: &[u8]) -> sha256::Hash {
+ let mut tlv_stream = TlvStream::new(&data[..]).peekable();
+ let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({
+ let mut engine = sha256::Hash::engine();
+ engine.input("LnNonce".as_bytes());
+ engine.input(tlv_stream.peek().unwrap().record_bytes);
+ engine
+ }));
+ let leaf_tag = tagged_hash_engine(sha256::Hash::hash("LnLeaf".as_bytes()));
+ let branch_tag = tagged_hash_engine(sha256::Hash::hash("LnBranch".as_bytes()));
+
+ let mut leaves = Vec::new();
+ for record in tlv_stream {
+ if !SIGNATURE_TYPES.contains(&record.r#type) {
+ leaves.push(tagged_hash_from_engine(leaf_tag.clone(), &record));
+ leaves.push(tagged_hash_from_engine(nonce_tag.clone(), &record.type_bytes));
+ }
+ }
+
+ // Calculate the merkle root hash in place.
+ let num_leaves = leaves.len();
+ for level in 0.. {
+ let step = 2 << level;
+ let offset = step / 2;
+ if offset >= num_leaves {
+ break;
+ }
+
+ let left_branches = (0..num_leaves).step_by(step);
+ let right_branches = (offset..num_leaves).step_by(step);
+ for (i, j) in left_branches.zip(right_branches) {
+ leaves[i] = tagged_branch_hash_from_engine(branch_tag.clone(), leaves[i], leaves[j]);
+ }
+ }
+
+ *leaves.first().unwrap()
+}
+
+fn tagged_hash<T: AsRef<[u8]>>(tag: sha256::Hash, msg: T) -> sha256::Hash {
+ let engine = tagged_hash_engine(tag);
+ tagged_hash_from_engine(engine, msg)
+}
+
+fn tagged_hash_engine(tag: sha256::Hash) -> sha256::HashEngine {
+ let mut engine = sha256::Hash::engine();
+ engine.input(tag.as_ref());
+ engine.input(tag.as_ref());
+ engine
+}
+
+fn tagged_hash_from_engine<T: AsRef<[u8]>>(mut engine: sha256::HashEngine, msg: T) -> sha256::Hash {
+ engine.input(msg.as_ref());
+ sha256::Hash::from_engine(engine)
+}
+
+fn tagged_branch_hash_from_engine(
+ mut engine: sha256::HashEngine, leaf1: sha256::Hash, leaf2: sha256::Hash,
+) -> sha256::Hash {
+ if leaf1 < leaf2 {
+ engine.input(leaf1.as_ref());
+ engine.input(leaf2.as_ref());
+ } else {
+ engine.input(leaf2.as_ref());
+ engine.input(leaf1.as_ref());
+ };
+ sha256::Hash::from_engine(engine)
+}
+
+/// [`Iterator`] over a sequence of bytes yielding [`TlvRecord`]s. The input is assumed to be a
+/// well-formed TLV stream.
+struct TlvStream<'a> {
+ data: io::Cursor<&'a [u8]>,
+}
+
+impl<'a> TlvStream<'a> {
+ fn new(data: &'a [u8]) -> Self {
+ Self {
+ data: io::Cursor::new(data),
+ }
+ }
+}
+
+/// A slice into a [`TlvStream`] for a record.
+struct TlvRecord<'a> {
+ r#type: u64,
+ type_bytes: &'a [u8],
+ // The entire TLV record.
+ record_bytes: &'a [u8],
+}
+
+impl AsRef<[u8]> for TlvRecord<'_> {
+ fn as_ref(&self) -> &[u8] { &self.record_bytes }
+}
+
+impl<'a> Iterator for TlvStream<'a> {
+ type Item = TlvRecord<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.data.position() < self.data.get_ref().len() as u64 {
+ let start = self.data.position();
+
+ let r#type = <BigSize as Readable>::read(&mut self.data).unwrap().0;
+ let offset = self.data.position();
+ let type_bytes = &self.data.get_ref()[start as usize..offset as usize];
+
+ let length = <BigSize as Readable>::read(&mut self.data).unwrap().0;
+ let offset = self.data.position();
+ let end = offset + length;
+
+ let _value = &self.data.get_ref()[offset as usize..end as usize];
+ let record_bytes = &self.data.get_ref()[start as usize..end as usize];
+
+ self.data.set_position(end);
+
+ Some(TlvRecord { r#type, type_bytes, record_bytes })
+ } else {
+ None
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use bitcoin::hashes::{Hash, sha256};
+ use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
+ use core::convert::Infallible;
+ use crate::offers::offer::{Amount, OfferBuilder};
+ use crate::offers::invoice_request::InvoiceRequest;
+ use crate::offers::parse::Bech32Encode;
+
+ #[test]
+ fn calculates_merkle_root_hash() {
+ // BOLT 12 test vectors
+ macro_rules! tlv1 { () => { "010203e8" } }
+ macro_rules! tlv2 { () => { "02080000010000020003" } }
+ macro_rules! tlv3 { () => { "03310266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351800000000000000010000000000000002" } }
+ assert_eq!(
+ super::root_hash(&hex::decode(tlv1!()).unwrap()),
+ sha256::Hash::from_slice(&hex::decode("b013756c8fee86503a0b4abdab4cddeb1af5d344ca6fc2fa8b6c08938caa6f93").unwrap()).unwrap(),
+ );
+ assert_eq!(
+ super::root_hash(&hex::decode(concat!(tlv1!(), tlv2!())).unwrap()),
+ sha256::Hash::from_slice(&hex::decode("c3774abbf4815aa54ccaa026bff6581f01f3be5fe814c620a252534f434bc0d1").unwrap()).unwrap(),
+ );
+ assert_eq!(
+ super::root_hash(&hex::decode(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap()),
+ sha256::Hash::from_slice(&hex::decode("ab2e79b1283b0b31e0b035258de23782df6b89a38cfa7237bde69aed1a658c5d").unwrap()).unwrap(),
+ );
+ }
+
+ #[test]
+ fn calculates_merkle_root_hash_from_invoice_request() {
+ let secp_ctx = Secp256k1::new();
+ let recipient_pubkey = {
+ let secret_key = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()).unwrap();
+ KeyPair::from_secret_key(&secp_ctx, &secret_key).public_key()
+ };
+ let payer_keys = {
+ let secret_key = SecretKey::from_slice(&hex::decode("4242424242424242424242424242424242424242424242424242424242424242").unwrap()).unwrap();
+ KeyPair::from_secret_key(&secp_ctx, &secret_key)
+ };
+
+ // BOLT 12 test vectors
+ let invoice_request = OfferBuilder::new("A Mathematical Treatise".into(), recipient_pubkey)
+ .amount(Amount::Currency { iso4217_code: *b"USD", amount: 100 })
+ .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)))
+ .unwrap();
+ assert_eq!(
+ invoice_request.to_string(),
+ "lnr1qqyqqqqqqqqqqqqqqcp4256ypqqkgzshgysy6ct5dpjk6ct5d93kzmpq23ex2ct5d9ek293pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpjkppqvjx204vgdzgsqpvcp4mldl3plscny0rt707gvpdh6ndydfacz43euzqhrurageg3n7kafgsek6gz3e9w52parv8gs2hlxzk95tzeswywffxlkeyhml0hh46kndmwf4m6xma3tkq2lu04qz3slje2rfthc89vss",
+ );
+ assert_eq!(
+ super::root_hash(&invoice_request.bytes[..]),
+ sha256::Hash::from_slice(&hex::decode("608407c18ad9a94d9ea2bcdbe170b6c20c462a7833a197621c916f78cf18e624").unwrap()).unwrap(),
+ );
+ }
+
+ impl AsRef<[u8]> for InvoiceRequest {
+ fn as_ref(&self) -> &[u8] {
+ &self.bytes
+ }
+ }
+
+ impl Bech32Encode for InvoiceRequest {
+ const BECH32_HRP: &'static str = "lnr";
+ }
+
+ impl core::fmt::Display for InvoiceRequest {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ self.fmt_bech32_str(f)
+ }
+ }
+}
//!
//! Offers are a flexible protocol for Lightning payments.
+pub mod invoice_request;
+mod merkle;
pub mod offer;
pub mod parse;
+mod payer;
use crate::io;
use crate::ln::features::OfferFeatures;
use crate::ln::msgs::MAX_VALUE_MSAT;
+use crate::offers::invoice_request::InvoiceRequestBuilder;
use crate::offers::parse::{Bech32Encode, ParseError, ParsedMessage, SemanticError};
use crate::onion_message::BlindedPath;
use crate::util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer};
/// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
///
/// Successive calls to this method will override the previous setting.
- pub fn amount_msats(mut self, amount_msats: u64) -> Self {
+ pub fn amount_msats(self, amount_msats: u64) -> Self {
self.amount(Amount::Bitcoin { amount_msats })
}
/// Sets the [`Offer::amount`].
///
/// Successive calls to this method will override the previous setting.
- fn amount(mut self, amount: Amount) -> Self {
+ pub(super) fn amount(mut self, amount: Amount) -> Self {
self.offer.amount = Some(amount);
self
}
- /// Sets the [`Offer::features`].
- ///
- /// Successive calls to this method will override the previous setting.
- #[cfg(test)]
- pub fn features(mut self, features: OfferFeatures) -> Self {
- self.offer.features = features;
- self
- }
-
/// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
/// already passed is valid and can be checked for using [`Offer::is_expired`].
///
}
}
+#[cfg(test)]
+impl OfferBuilder {
+ fn features_unchecked(mut self, features: OfferFeatures) -> Self {
+ self.offer.features = features;
+ self
+ }
+
+ pub(super) fn build_unchecked(self) -> Offer {
+ let mut bytes = Vec::new();
+ self.offer.write(&mut bytes).unwrap();
+
+ Offer { bytes, contents: self.offer }
+ }
+}
+
/// An `Offer` is a potentially long-lived proposal for payment of a good or service.
///
-/// An offer is a precursor to an `InvoiceRequest`. A merchant publishes an offer from which a
+/// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a
/// customer may request an `Invoice` for a specific quantity and using an amount sufficient to
/// cover that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`].
///
/// latter.
///
/// Through the use of [`BlindedPath`]s, offers provide recipient privacy.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
#[derive(Clone, Debug)]
pub struct Offer {
// The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
// fields.
- bytes: Vec<u8>,
- contents: OfferContents,
+ pub(super) bytes: Vec<u8>,
+ pub(super) contents: OfferContents,
}
-/// The contents of an [`Offer`], which may be shared with an `InvoiceRequest` or an `Invoice`.
+/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or an `Invoice`.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
#[derive(Clone, Debug)]
-pub(crate) struct OfferContents {
+pub(super) struct OfferContents {
chains: Option<Vec<ChainHash>>,
metadata: Option<Vec<u8>>,
amount: Option<Amount>,
/// 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
- .as_ref()
- .cloned()
- .unwrap_or_else(|| vec![self.contents.implied_chain()])
+ 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)
}
// TODO: Link to corresponding method in `InvoiceRequest`.
/// The minimum amount required for a successful payment of a single item.
pub fn amount(&self) -> Option<&Amount> {
- self.contents.amount.as_ref()
+ self.contents.amount()
}
/// A complete description of the purpose of the payment. Intended to be displayed to the user
self.contents.supported_quantity()
}
+ /// Returns whether the given quantity is valid for the offer.
+ pub fn is_valid_quantity(&self, quantity: u64) -> bool {
+ self.contents.is_valid_quantity(quantity)
+ }
+
+ /// Returns whether a quantity is expected in an [`InvoiceRequest`] for the offer.
+ ///
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ pub fn expects_quantity(&self) -> bool {
+ self.contents.expects_quantity()
+ }
+
/// The public key used by the recipient to sign invoices.
pub fn signing_pubkey(&self) -> PublicKey {
self.contents.signing_pubkey.unwrap()
}
+ /// Creates an [`InvoiceRequest`] for the offer with the given `metadata` and `payer_id`, which
+ /// will be reflected in the `Invoice` response.
+ ///
+ /// The `metadata` is useful for including information about the derivation of `payer_id` such
+ /// that invoice response handling can be stateless. Also serves as payer-provided entropy while
+ /// hashing in the signature calculation.
+ ///
+ /// This should not leak any information such as by using a simple BIP-32 derivation path.
+ /// Otherwise, payments may be correlated.
+ ///
+ /// Errors if the offer contains unknown required features.
+ ///
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ pub fn request_invoice(
+ &self, metadata: Vec<u8>, payer_id: PublicKey
+ ) -> Result<InvoiceRequestBuilder, SemanticError> {
+ if self.features().requires_unknown_bits() {
+ return Err(SemanticError::UnknownRequiredFeatures);
+ }
+
+ Ok(InvoiceRequestBuilder::new(self, metadata, payer_id))
+ }
+
#[cfg(test)]
- fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+ pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
self.contents.as_tlv_stream()
}
}
}
impl OfferContents {
+ pub fn chains(&self) -> Vec<ChainHash> {
+ self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
+ }
+
pub fn implied_chain(&self) -> ChainHash {
ChainHash::using_genesis_block(Network::Bitcoin)
}
+ pub fn supports_chain(&self, chain: ChainHash) -> bool {
+ self.chains().contains(&chain)
+ }
+
+ pub fn amount(&self) -> Option<&Amount> {
+ self.amount.as_ref()
+ }
+
+ pub(super) fn check_amount_msats_for_quantity(
+ &self, amount_msats: Option<u64>, quantity: Option<u64>
+ ) -> Result<(), SemanticError> {
+ let offer_amount_msats = match self.amount {
+ None => 0,
+ Some(Amount::Bitcoin { amount_msats }) => amount_msats,
+ Some(Amount::Currency { .. }) => return Err(SemanticError::UnsupportedCurrency),
+ };
+
+ if !self.expects_quantity() || quantity.is_some() {
+ let expected_amount_msats = offer_amount_msats * quantity.unwrap_or(1);
+ let amount_msats = amount_msats.unwrap_or(expected_amount_msats);
+
+ if amount_msats < expected_amount_msats {
+ return Err(SemanticError::InsufficientAmount);
+ }
+
+ if amount_msats > MAX_VALUE_MSAT {
+ return Err(SemanticError::InvalidAmount);
+ }
+ }
+
+ Ok(())
+ }
+
pub fn supported_quantity(&self) -> Quantity {
self.supported_quantity
}
- fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+ pub(super) fn check_quantity(&self, quantity: Option<u64>) -> Result<(), SemanticError> {
+ let expects_quantity = self.expects_quantity();
+ match quantity {
+ None if expects_quantity => Err(SemanticError::MissingQuantity),
+ Some(_) if !expects_quantity => Err(SemanticError::UnexpectedQuantity),
+ Some(quantity) if !self.is_valid_quantity(quantity) => {
+ Err(SemanticError::InvalidQuantity)
+ },
+ _ => Ok(()),
+ }
+ }
+
+ fn is_valid_quantity(&self, quantity: u64) -> bool {
+ match self.supported_quantity {
+ Quantity::Bounded(n) => {
+ let n = n.get();
+ if n == 1 { false }
+ else { quantity > 0 && quantity <= n }
+ },
+ Quantity::Unbounded => quantity > 0,
+ }
+ }
+
+ fn expects_quantity(&self) -> bool {
+ match self.supported_quantity {
+ Quantity::Bounded(n) => n.get() != 1,
+ Quantity::Unbounded => true,
+ }
+ }
+
+ pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
let (currency, amount) = match &self.amount {
None => (None, None),
Some(Amount::Bitcoin { amount_msats }) => (None, Some(*amount_msats)),
assert_eq!(offer.bytes, buffer.as_slice());
assert_eq!(offer.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+ assert!(offer.supports_chain(ChainHash::using_genesis_block(Network::Bitcoin)));
assert_eq!(offer.metadata(), None);
assert_eq!(offer.amount(), None);
assert_eq!(offer.description(), PrintableString("foo"));
.chain(Network::Bitcoin)
.build()
.unwrap();
+ assert!(offer.supports_chain(mainnet));
assert_eq!(offer.chains(), vec![mainnet]);
assert_eq!(offer.as_tlv_stream().chains, None);
.chain(Network::Testnet)
.build()
.unwrap();
+ assert!(offer.supports_chain(testnet));
assert_eq!(offer.chains(), vec![testnet]);
assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
.chain(Network::Testnet)
.build()
.unwrap();
+ assert!(offer.supports_chain(testnet));
assert_eq!(offer.chains(), vec![testnet]);
assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
.chain(Network::Testnet)
.build()
.unwrap();
+ assert!(offer.supports_chain(mainnet));
+ assert!(offer.supports_chain(testnet));
assert_eq!(offer.chains(), vec![mainnet, testnet]);
assert_eq!(offer.as_tlv_stream().chains, Some(&vec![mainnet, testnet]));
}
#[test]
fn builds_offer_with_features() {
let offer = OfferBuilder::new("foo".into(), pubkey(42))
- .features(OfferFeatures::unknown())
+ .features_unchecked(OfferFeatures::unknown())
.build()
.unwrap();
assert_eq!(offer.features(), &OfferFeatures::unknown());
assert_eq!(offer.as_tlv_stream().features, Some(&OfferFeatures::unknown()));
let offer = OfferBuilder::new("foo".into(), pubkey(42))
- .features(OfferFeatures::unknown())
- .features(OfferFeatures::empty())
+ .features_unchecked(OfferFeatures::unknown())
+ .features_unchecked(OfferFeatures::empty())
.build()
.unwrap();
assert_eq!(offer.features(), &OfferFeatures::empty());
assert_eq!(tlv_stream.quantity_max, None);
}
+ #[test]
+ fn fails_requesting_invoice_with_unknown_required_features() {
+ match OfferBuilder::new("foo".into(), pubkey(42))
+ .features_unchecked(OfferFeatures::unknown())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], pubkey(43))
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::UnknownRequiredFeatures),
+ }
+ }
+
#[test]
fn parses_offer_with_chains() {
let offer = OfferBuilder::new("foo".into(), pubkey(42))
use bitcoin::bech32;
use bitcoin::bech32::{FromBase32, ToBase32};
+use bitcoin::secp256k1;
use core::convert::TryFrom;
use core::fmt;
use crate::io;
use crate::prelude::*;
/// Indicates a message can be encoded using bech32.
-pub(crate) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
+pub(super) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
/// Human readable part of the message's bech32 encoding.
const BECH32_HRP: &'static str;
/// A wrapper for reading a message as a TLV stream `T` from a byte sequence, while still
/// maintaining ownership of the bytes for later use.
-pub(crate) struct ParsedMessage<T: SeekReadable> {
+pub(super) struct ParsedMessage<T: SeekReadable> {
pub bytes: Vec<u8>,
pub tlv_stream: T,
}
Decode(DecodeError),
/// The parsed message has invalid semantics.
InvalidSemantics(SemanticError),
+ /// The parsed message has an invalid signature.
+ InvalidSignature(secp256k1::Error),
}
/// Error when interpreting a TLV stream as a specific type.
#[derive(Debug, PartialEq)]
pub enum SemanticError {
+ /// The current [`std::time::SystemTime`] is past the offer or invoice's expiration.
+ AlreadyExpired,
+ /// The provided chain hash does not correspond to a supported chain.
+ UnsupportedChain,
/// An amount was expected but was missing.
MissingAmount,
/// The amount exceeded the total bitcoin supply.
InvalidAmount,
+ /// An amount was provided but was not sufficient in value.
+ InsufficientAmount,
/// A currency was provided that is not supported.
UnsupportedCurrency,
+ /// A feature was required but is unknown.
+ UnknownRequiredFeatures,
/// A required description was not provided.
MissingDescription,
/// A signing pubkey was not provided.
MissingSigningPubkey,
+ /// A quantity was expected but was missing.
+ MissingQuantity,
/// An unsupported quantity was provided.
InvalidQuantity,
+ /// A quantity or quantity bounds was provided but was not expected.
+ UnexpectedQuantity,
+ /// Payer metadata was expected but was missing.
+ MissingPayerMetadata,
+ /// A payer id was expected but was missing.
+ MissingPayerId,
}
impl From<bech32::Error> for ParseError {
Self::InvalidSemantics(error)
}
}
+
+impl From<secp256k1::Error> for ParseError {
+ fn from(error: secp256k1::Error) -> Self {
+ Self::InvalidSignature(error)
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Data structures and encoding for `invoice_request_metadata` records.
+
+use crate::util::ser::WithoutLength;
+
+use crate::prelude::*;
+
+/// An unpredictable sequence of bytes typically containing information needed to derive
+/// [`InvoiceRequest::payer_id`].
+///
+/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
+#[derive(Clone, Debug)]
+pub(super) struct PayerContents(pub Vec<u8>);
+
+tlv_stream!(PayerTlvStream, PayerTlvStreamRef, 0..1, {
+ (0, metadata: (Vec<u8>, WithoutLength)),
+});
((v[5] as u64) << 8*0)
}
#[inline]
-pub fn slice_to_be64(v: &[u8]) -> u64 {
- ((v[0] as u64) << 8*7) |
- ((v[1] as u64) << 8*6) |
- ((v[2] as u64) << 8*5) |
- ((v[3] as u64) << 8*4) |
- ((v[4] as u64) << 8*3) |
- ((v[5] as u64) << 8*2) |
- ((v[6] as u64) << 8*1) |
- ((v[7] as u64) << 8*0)
-}
-
-#[inline]
-pub fn be16_to_array(u: u16) -> [u8; 2] {
- let mut v = [0; 2];
- v[0] = ((u >> 8*1) & 0xff) as u8;
- v[1] = ((u >> 8*0) & 0xff) as u8;
- v
-}
-#[inline]
-pub fn be32_to_array(u: u32) -> [u8; 4] {
- let mut v = [0; 4];
- v[0] = ((u >> 8*3) & 0xff) as u8;
- v[1] = ((u >> 8*2) & 0xff) as u8;
- v[2] = ((u >> 8*1) & 0xff) as u8;
- v[3] = ((u >> 8*0) & 0xff) as u8;
- v
-}
-#[inline]
pub fn be48_to_array(u: u64) -> [u8; 6] {
assert!(u & 0xffff_0000_0000_0000 == 0);
let mut v = [0; 6];
v[5] = ((u >> 8*0) & 0xff) as u8;
v
}
-#[inline]
-pub fn be64_to_array(u: u64) -> [u8; 8] {
- let mut v = [0; 8];
- v[0] = ((u >> 8*7) & 0xff) as u8;
- v[1] = ((u >> 8*6) & 0xff) as u8;
- v[2] = ((u >> 8*5) & 0xff) as u8;
- v[3] = ((u >> 8*4) & 0xff) as u8;
- v[4] = ((u >> 8*3) & 0xff) as u8;
- v[5] = ((u >> 8*2) & 0xff) as u8;
- v[6] = ((u >> 8*1) & 0xff) as u8;
- v[7] = ((u >> 8*0) & 0xff) as u8;
- v
-}
#[cfg(test)]
mod tests {
use super::*;
-
+
#[test]
fn test_all() {
assert_eq!(slice_to_be48(&[0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad]), 0xdeadbeef1bad);
- assert_eq!(slice_to_be64(&[0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad, 0x1d, 0xea]), 0xdeadbeef1bad1dea);
- assert_eq!(be16_to_array(0xdead), [0xde, 0xad]);
- assert_eq!(be32_to_array(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
assert_eq!(be48_to_array(0xdeadbeef1bad), [0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad]);
- assert_eq!(be64_to_array(0xdeadbeef1bad1dea), [0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad, 0x1d, 0xea]);
}
}
use bitcoin::secp256k1;
use bitcoin::secp256k1::{SecretKey, PublicKey};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
+#[cfg(anchors)]
+use crate::util::events::HTLCDescriptor;
use crate::util::ser::{Writeable, Writer};
use crate::io::Error;
Ok(self.inner.sign_justice_revoked_htlc(justice_tx, input, amount, per_commitment_key, htlc, secp_ctx).unwrap())
}
+ #[cfg(anchors)]
+ fn sign_holder_htlc_transaction(
+ &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
+ secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()> {
+ let per_commitment_point = self.get_per_commitment_point(htlc_descriptor.per_commitment_number, secp_ctx);
+ assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input());
+ assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(&per_commitment_point, secp_ctx));
+ Ok(self.inner.sign_holder_htlc_transaction(htlc_tx, input, htlc_descriptor, secp_ctx).unwrap())
+ }
+
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
Ok(self.inner.sign_counterparty_htlc_transaction(htlc_tx, input, amount, per_commitment_point, htlc, secp_ctx).unwrap())
}
self.inner.sign_channel_announcement(msg, secp_ctx)
}
- fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) {
- self.inner.ready_channel(channel_parameters)
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
+ self.inner.provide_channel_parameters(channel_parameters)
}
}
},
/// A malformed Route was provided (eg overflowed value, node id mismatch, overly-looped route,
/// too-many-hops, etc).
- RouteError {
+ InvalidRoute {
/// A human-readable error message
err: &'static str
},
match *self {
APIError::APIMisuseError {ref err} => write!(f, "Misuse error: {}", err),
APIError::FeeRateTooHigh {ref err, ref feerate} => write!(f, "{} feerate: {}", err, feerate),
- APIError::RouteError {ref err} => write!(f, "Route error: {}", err),
+ APIError::InvalidRoute {ref err} => write!(f, "Invalid route provided: {}", err),
APIError::ChannelUnavailable {ref err} => write!(f, "Channel unavailable: {}", err),
APIError::MonitorUpdateInProgress => f.write_str("Client indicated a channel monitor update is in progress but not yet complete"),
APIError::IncompatibleShutdownScript { ref script } => {
use crate::chain::keysinterface::SpendableOutputDescriptor;
#[cfg(anchors)]
-use crate::ln::chan_utils::HTLCOutputInCommitment;
+use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment};
use crate::ln::channelmanager::{InterceptId, PaymentId};
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
use crate::ln::features::ChannelTypeFeatures;
use bitcoin::{PackedLockTime, Transaction};
#[cfg(anchors)]
-use bitcoin::OutPoint;
+use bitcoin::{OutPoint, Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::PublicKey;
+#[cfg(anchors)]
+use bitcoin::secp256k1::{self, Secp256k1};
+#[cfg(anchors)]
+use bitcoin::secp256k1::ecdsa::Signature;
use crate::io;
use crate::prelude::*;
use core::time::Duration;
pub outpoint: OutPoint,
}
+#[cfg(anchors)]
+/// A descriptor used to sign for a commitment transaction's HTLC output.
+#[derive(Clone, Debug)]
+pub struct HTLCDescriptor {
+ /// A unique identifier used along with `channel_value_satoshis` to re-derive the
+ /// [`InMemorySigner`] required to sign `input`.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ pub channel_keys_id: [u8; 32],
+ /// The value in satoshis of the channel we're attempting to spend the anchor output of. This is
+ /// used along with `channel_keys_id` to re-derive the [`InMemorySigner`] required to sign
+ /// `input`.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ pub channel_value_satoshis: u64,
+ /// The necessary channel parameters that need to be provided to the re-derived
+ /// [`InMemorySigner`] through [`BaseSign::ready_channel`].
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`BaseSign::ready_channel`]: crate::chain::keysinterface::BaseSign::ready_channel
+ pub channel_parameters: ChannelTransactionParameters,
+ /// The txid of the commitment transaction in which the HTLC output lives.
+ pub commitment_txid: Txid,
+ /// The number of the commitment transaction in which the HTLC output lives.
+ pub per_commitment_number: u64,
+ /// The details of the HTLC as it appears in the commitment transaction.
+ pub htlc: HTLCOutputInCommitment,
+ /// The preimage, if `Some`, to claim the HTLC output with. If `None`, the timeout path must be
+ /// taken.
+ pub preimage: Option<PaymentPreimage>,
+ /// The counterparty's signature required to spend the HTLC output.
+ pub counterparty_sig: Signature
+}
+
+#[cfg(anchors)]
+impl HTLCDescriptor {
+ /// Returns the unsigned transaction input spending the HTLC output in the commitment
+ /// transaction.
+ pub fn unsigned_tx_input(&self) -> TxIn {
+ chan_utils::build_htlc_input(&self.commitment_txid, &self.htlc, true /* opt_anchors */)
+ }
+
+ /// Returns the delayed output created as a result of spending the HTLC output in the commitment
+ /// transaction.
+ pub fn tx_output<C: secp256k1::Signing + secp256k1::Verification>(
+ &self, per_commitment_point: &PublicKey, secp: &Secp256k1<C>
+ ) -> TxOut {
+ let channel_params = self.channel_parameters.as_holder_broadcastable();
+ let broadcaster_keys = channel_params.broadcaster_pubkeys();
+ let counterparty_keys = channel_params.countersignatory_pubkeys();
+ let broadcaster_delayed_key = chan_utils::derive_public_key(
+ secp, per_commitment_point, &broadcaster_keys.delayed_payment_basepoint
+ );
+ let counterparty_revocation_key = chan_utils::derive_public_revocation_key(
+ secp, per_commitment_point, &counterparty_keys.revocation_basepoint
+ );
+ chan_utils::build_htlc_output(
+ 0 /* feerate_per_kw */, channel_params.contest_delay(), &self.htlc, true /* opt_anchors */,
+ false /* use_non_zero_fee_anchors */, &broadcaster_delayed_key, &counterparty_revocation_key
+ )
+ }
+
+ /// Returns the witness script of the HTLC output in the commitment transaction.
+ pub fn witness_script<C: secp256k1::Signing + secp256k1::Verification>(
+ &self, per_commitment_point: &PublicKey, secp: &Secp256k1<C>
+ ) -> Script {
+ let channel_params = self.channel_parameters.as_holder_broadcastable();
+ let broadcaster_keys = channel_params.broadcaster_pubkeys();
+ let counterparty_keys = channel_params.countersignatory_pubkeys();
+ let broadcaster_htlc_key = chan_utils::derive_public_key(
+ secp, per_commitment_point, &broadcaster_keys.htlc_basepoint
+ );
+ let counterparty_htlc_key = chan_utils::derive_public_key(
+ secp, per_commitment_point, &counterparty_keys.htlc_basepoint
+ );
+ let counterparty_revocation_key = chan_utils::derive_public_revocation_key(
+ secp, per_commitment_point, &counterparty_keys.revocation_basepoint
+ );
+ chan_utils::get_htlc_redeemscript_with_explicit_keys(
+ &self.htlc, true /* opt_anchors */, &broadcaster_htlc_key, &counterparty_htlc_key,
+ &counterparty_revocation_key,
+ )
+ }
+
+ /// Returns the fully signed witness required to spend the HTLC output in the commitment
+ /// transaction.
+ pub fn tx_input_witness(&self, signature: &Signature, witness_script: &Script) -> Witness {
+ chan_utils::build_htlc_input_witness(
+ signature, &self.counterparty_sig, &self.preimage, witness_script, true /* opt_anchors */
+ )
+ }
+}
+
#[cfg(anchors)]
/// Represents the different types of transactions, originating from LDK, to be bumped.
#[derive(Clone, Debug)]
/// with additional inputs to meet the target feerate. Failure to meet the target feerate
/// decreases the confirmation odds of the transaction package (which includes the commitment
/// and child anchor transactions), possibly resulting in a loss of funds. Once the transaction
- /// is constructed, it must be fully signed for and broadcasted by the consumer of the event
+ /// is constructed, it must be fully signed for and broadcast by the consumer of the event
/// along with the `commitment_tx` enclosed. Note that the `commitment_tx` must always be
/// broadcast first, as the child anchor transaction depends on it.
///
/// The consumer should be able to sign for any of the additional inputs included within the
/// child anchor transaction. To sign its anchor input, an [`InMemorySigner`] should be
/// re-derived through [`KeysManager::derive_channel_keys`] with the help of
- /// [`AnchorDescriptor::channel_keys_id`] and [`AnchorDescriptor::channel_value_satoshis`].
+ /// [`AnchorDescriptor::channel_keys_id`] and [`AnchorDescriptor::channel_value_satoshis`]. The
+ /// anchor input signature can be computed with [`BaseSign::sign_holder_anchor_input`],
+ /// which can then be provided to [`build_anchor_input_witness`] along with the `funding_pubkey`
+ /// to obtain the full witness required to spend.
///
/// It is possible to receive more than one instance of this event if a valid child anchor
/// transaction is never broadcast or is but not with a sufficient fee to be mined. Care should
///
/// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
/// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
+ /// [`BaseSign::sign_holder_anchor_input`]: crate::chain::keysinterface::BaseSign::sign_holder_anchor_input
+ /// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {
/// The target feerate that the transaction package, which consists of the commitment
/// transaction and the to-be-crafted child anchor transaction, must meet.
/// commitment transaction confirms.
pending_htlcs: Vec<HTLCOutputInCommitment>,
},
+ /// Indicates that a channel featuring anchor outputs has unilaterally closed on-chain by a
+ /// holder commitment transaction and its HTLC(s) need to be resolved on-chain. With the
+ /// zero-HTLC-transaction-fee variant of anchor outputs, the pre-signed HTLC
+ /// transactions have a zero fee, thus requiring additional inputs and/or outputs to be attached
+ /// for a timely confirmation within the chain. These additional inputs and/or outputs must be
+ /// appended to the resulting HTLC transaction to meet the target feerate. Failure to meet the
+ /// target feerate decreases the confirmation odds of the transaction, possibly resulting in a
+ /// loss of funds. Once the transaction meets the target feerate, it must be signed for and
+ /// broadcast by the consumer of the event.
+ ///
+ /// The consumer should be able to sign for any of the non-HTLC inputs added to the resulting
+ /// HTLC transaction. To sign HTLC inputs, an [`InMemorySigner`] should be re-derived through
+ /// [`KeysManager::derive_channel_keys`] with the help of `channel_keys_id` and
+ /// `channel_value_satoshis`. Each HTLC input's signature can be computed with
+ /// [`BaseSign::sign_holder_htlc_transaction`], which can then be provided to
+ /// [`HTLCDescriptor::tx_input_witness`] to obtain the fully signed witness required to spend.
+ ///
+ /// It is possible to receive more than one instance of this event if a valid HTLC transaction
+ /// is never broadcast or is but not with a sufficient fee to be mined. Care should be taken by
+ /// the consumer of the event to ensure any future iterations of the HTLC transaction adhere to
+ /// the [Replace-By-Fee
+ /// rules](https://github.com/bitcoin/bitcoin/blob/master/doc/policy/mempool-replacements.md)
+ /// for fee bumps to be accepted into the mempool, and eventually the chain. As the frequency of
+ /// these events is not user-controlled, users may ignore/drop the event if either they are no
+ /// longer able to commit external confirmed funds to the HTLC transaction or the fee committed
+ /// to the HTLC transaction is greater in value than the HTLCs being claimed.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
+ /// [`BaseSign::sign_holder_htlc_transaction`]: crate::chain::keysinterface::BaseSign::sign_holder_htlc_transaction
+ /// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
+ HTLCResolution {
+ target_feerate_sat_per_1000_weight: u32,
+ htlc_descriptors: Vec<HTLCDescriptor>,
+ },
}
/// Will be used in [`Event::HTLCIntercepted`] to identify the next hop in the HTLC's path.
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
user_channel_id: u128,
},
- /// Indicates we've received (an offer of) money! Just gotta dig out that payment preimage and
- /// feed it to [`ChannelManager::claim_funds`] to get it....
+ /// Indicates that we've been offered a payment and it needs to be claimed via calling
+ /// [`ChannelManager::claim_funds`] with the preimage given in [`PaymentPurpose`].
///
/// Note that if the preimage is not known, you should call
/// [`ChannelManager::fail_htlc_backwards`] to free up resources for this HTLC and avoid
///
/// # Note
/// LDK will not stop an inbound payment from being paid multiple times, so multiple
- /// `PaymentReceived` events may be generated for the same payment.
+ /// `PaymentClaimable` events may be generated for the same payment.
+ ///
+ /// # Note
+ /// This event used to be called `PaymentReceived` in LDK versions 0.0.112 and earlier.
///
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
/// [`ChannelManager::fail_htlc_backwards`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards
- PaymentReceived {
- /// The node that received the payment.
- /// This is useful to identify payments which were received via [phantom node payments].
+ PaymentClaimable {
+ /// The node that will receive the payment after it has been claimed.
+ /// This is useful to identify payments received via [phantom nodes].
/// This field will always be filled in when the event was generated by LDK versions
/// 0.0.113 and above.
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom nodes]: crate::chain::keysinterface::PhantomKeysManager
receiver_node_id: Option<PublicKey>,
/// The hash for which the preimage should be handed to the ChannelManager. Note that LDK will
/// not stop you from registering duplicate payment hashes for inbound payments.
/// Indicates a payment has been claimed and we've received money!
///
/// This most likely occurs when [`ChannelManager::claim_funds`] has been called in response
- /// to an [`Event::PaymentReceived`]. However, if we previously crashed during a
+ /// to an [`Event::PaymentClaimable`]. However, if we previously crashed during a
/// [`ChannelManager::claim_funds`] call you may see this event without a corresponding
- /// [`Event::PaymentReceived`] event.
+ /// [`Event::PaymentClaimable`] event.
///
/// # Note
/// LDK will not stop an inbound payment from being paid multiple times, so multiple
- /// `PaymentReceived` events may be generated for the same payment. If you then call
- /// [`ChannelManager::claim_funds`] twice for the same [`Event::PaymentReceived`] you may get
+ /// `PaymentClaimable` events may be generated for the same payment. If you then call
+ /// [`ChannelManager::claim_funds`] twice for the same [`Event::PaymentClaimable`] you may get
/// multiple `PaymentClaimed` events.
///
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
PaymentClaimed {
/// The node that received the payment.
- /// This is useful to identify payments which were received via [phantom node payments].
+ /// This is useful to identify payments which were received via [phantom nodes].
/// This field will always be filled in when the event was generated by LDK versions
/// 0.0.113 and above.
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom nodes]: crate::chain::keysinterface::PhantomKeysManager
receiver_node_id: Option<PublicKey>,
/// The payment hash of the claimed payment. Note that LDK will not stop you from
/// registering duplicate payment hashes for inbound payments.
payment_hash: PaymentHash,
/// The value, in thousandths of a satoshi, that this payment is for.
amount_msat: u64,
- /// The purpose of this claimed payment, i.e. whether the payment was for an invoice or a
+ /// The purpose of the claimed payment, i.e. whether the payment was for an invoice or a
/// spontaneous payment.
purpose: PaymentPurpose,
},
// We never write out FundingGenerationReady events as, upon disconnection, peers
// drop any channels which have not yet exchanged funding_signed.
},
- &Event::PaymentReceived { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
+ &Event::PaymentClaimable { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
1u8.write(writer)?;
let mut payment_secret = None;
let payment_preimage;
&Event::BumpTransaction(ref event)=> {
27u8.write(writer)?;
match event {
- // We never write the ChannelClose events as they'll be replayed upon restarting
- // anyway if the commitment transaction remains unconfirmed.
+ // We never write the ChannelClose|HTLCResolution events as they'll be replayed
+ // upon restarting anyway if they remain unresolved.
BumpTransactionEvent::ChannelClose { .. } => {}
+ BumpTransactionEvent::HTLCResolution { .. } => {}
}
+ write_tlv_fields!(writer, {}); // Write a length field for forwards compat
}
&Event::ChannelReady { ref channel_id, ref user_channel_id, ref counterparty_node_id, ref channel_type } => {
29u8.write(writer)?;
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
None => return Err(msgs::DecodeError::InvalidValue),
};
- Ok(Some(Event::PaymentReceived {
+ Ok(Some(Event::PaymentClaimable {
receiver_node_id,
payment_hash,
amount_msat,
use core::ops::Deref;
use bitcoin::hashes::hex::ToHex;
-use crate::io::{self};
+use crate::io;
use crate::routing::scoring::WriteableScore;
-use crate::{chain::{keysinterface::{Sign, KeysInterface}, self, transaction::{OutPoint}, chaininterface::{BroadcasterInterface, FeeEstimator}, chainmonitor::{Persist, MonitorUpdateId}, channelmonitor::{ChannelMonitor, ChannelMonitorUpdate}}, ln::channelmanager::ChannelManager, routing::gossip::NetworkGraph};
+use crate::chain;
+use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
+use crate::chain::chainmonitor::{Persist, MonitorUpdateId};
+use crate::chain::keysinterface::{Sign, KeysInterface};
+use crate::chain::transaction::OutPoint;
+use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
+use crate::ln::channelmanager::ChannelManager;
+use crate::routing::gossip::NetworkGraph;
use super::{logger::Logger, ser::Writeable};
/// Trait for a key-value store for persisting some writeable object at some key
use core::ops::Deref;
use bitcoin::secp256k1::{PublicKey, SecretKey};
-use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
-use bitcoin::secp256k1::ecdsa::Signature;
+use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE, SCHNORR_SIGNATURE_SIZE};
+use bitcoin::secp256k1::ecdsa;
+use bitcoin::secp256k1::schnorr;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
impl_array!(16); // for IPv6
impl_array!(32); // for channel id & hmac
impl_array!(PUBLIC_KEY_SIZE); // for PublicKey
-impl_array!(COMPACT_SIGNATURE_SIZE); // for Signature
+impl_array!(64); // for ecdsa::Signature and schnorr::Signature
impl_array!(1300); // for OnionPacket.hop_data
impl Writeable for [u16; 8] {
Ok(ret)
}
}
-impl Writeable for Vec<Signature> {
+impl Writeable for Vec<ecdsa::Signature> {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.len() as u16).write(w)?;
}
}
-impl Readable for Vec<Signature> {
+impl Readable for Vec<ecdsa::Signature> {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let len: u16 = Readable::read(r)?;
}
}
-impl Writeable for Signature {
+impl Writeable for ecdsa::Signature {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.serialize_compact().write(w)
}
- #[inline]
- fn serialized_length(&self) -> usize {
- COMPACT_SIGNATURE_SIZE
- }
}
-impl Readable for Signature {
+impl Readable for ecdsa::Signature {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let buf: [u8; COMPACT_SIGNATURE_SIZE] = Readable::read(r)?;
- match Signature::from_compact(&buf) {
+ match ecdsa::Signature::from_compact(&buf) {
+ Ok(sig) => Ok(sig),
+ Err(_) => return Err(DecodeError::InvalidValue),
+ }
+ }
+}
+
+impl Writeable for schnorr::Signature {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.as_ref().write(w)
+ }
+}
+
+impl Readable for schnorr::Signature {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let buf: [u8; SCHNORR_SIGNATURE_SIZE] = Readable::read(r)?;
+ match schnorr::Signature::from_slice(&buf) {
Ok(sig) => Ok(sig),
Err(_) => return Err(DecodeError::InvalidValue),
}
$(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
}) => {
#[derive(Debug)]
- pub(crate) struct $name {
+ pub(super) struct $name {
$(
- $field: Option<tlv_record_type!($fieldty)>,
+ pub(super) $field: Option<tlv_record_type!($fieldty)>,
)*
}
- pub(crate) struct $nameref<'a> {
+ pub(super) struct $nameref<'a> {
$(
- pub(crate) $field: Option<tlv_record_ref_type!($fieldty)>,
+ pub(super) $field: Option<tlv_record_ref_type!($fieldty)>,
)*
}
Ok($st::$tuple_variant_name(Readable::read(reader)?))
}),*
_ => {
- Err(DecodeError::UnknownRequiredFeature)
+ Err($crate::ln::msgs::DecodeError::UnknownRequiredFeature)
},
}
}
fn get_inbound_payment_key_material(&self) -> KeyMaterial { unreachable!(); }
fn get_destination_script(&self) -> Script { unreachable!(); }
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
- fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> EnforcingSigner { unreachable!(); }
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] { unreachable!(); }
+ fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::Signer { unreachable!(); }
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
fn read_chan_signer(&self, mut reader: &[u8]) -> Result<Self::Signer, msgs::DecodeError> {
}
}
- fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> EnforcingSigner {
- let keys = self.backing.get_channel_signer(inbound, channel_value_satoshis);
+ fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+ self.backing.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> EnforcingSigner {
+ let keys = self.backing.derive_channel_signer(channel_value_satoshis, channel_keys_id);
let state = self.make_enforcement_state_cell(keys.commitment_seed);
EnforcingSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check)
}
--- /dev/null
+## API Updates
+
+## Backwards Compatibilty
+- The event formerly known as `PaymentReceived` is now called
+ `PaymentClaimable`.
--- /dev/null
+## API Updates
+- `ChannelManager::abandon_payment` docs have been updated to note that the
+ payment may return to pending after a restart if no persistence occurs. This
+ is not a change in behavior - ensure your existing code is safe.