Skip to content
133 changes: 133 additions & 0 deletions fuzz/fuzz-fake-hashes/src/bin/payer_proof_deser_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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.

// This file is auto-generated by gen_target.sh based on target_template.txt
// To modify it, modify target_template.txt and run gen_target.sh instead.

#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
#![cfg_attr(rustfmt, rustfmt_skip)]

#[cfg(not(fuzzing))]
compile_error!("Fuzz targets need cfg=fuzzing");

#[cfg(not(hashes_fuzz))]
compile_error!("Fuzz target does not support cfg(not(hashes_fuzz))");

#[cfg(not(secp256k1_fuzz))]
compile_error!("Fuzz targets need cfg=secp256k1_fuzz");

extern crate lightning_fuzz;
use lightning_fuzz::payer_proof_deser::*;
use lightning_fuzz::utils::test_logger;

#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
payer_proof_deser_test(&data, test_logger::DevNull {});
});
}

#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
payer_proof_deser_test(&data, test_logger::DevNull {});
});
}
}

#[cfg(feature = "libfuzzer_fuzz")]
#[macro_use] extern crate libfuzzer_sys;
#[cfg(feature = "libfuzzer_fuzz")]
fuzz_target!(|data: &[u8]| {
payer_proof_deser_test(data, test_logger::DevNull {});
});

#[cfg(feature = "stdin_fuzz")]
fn main() {
use std::io::Read;

// On macOS, panic=abort causes the process to send SIGABRT which can leave it
// stuck in an uninterruptible state due to the ReportCrash daemon. Using
// process::exit in a panic hook avoids this by terminating cleanly.
#[cfg(target_os = "macos")]
std::panic::set_hook(Box::new(|panic_info| {
use std::io::Write;
let _ = std::io::stdout().flush();
eprintln!("{}\n{}", panic_info, std::backtrace::Backtrace::force_capture());
let _ = std::io::stderr().flush();
std::process::exit(1);
}));

let mut data = Vec::with_capacity(8192);
std::io::stdin().read_to_end(&mut data).unwrap();
payer_proof_deser_test(&data, test_logger::Stdout {});
}

#[test]
fn run_test_cases() {
use std::fs;
use std::io::Read;
use lightning_fuzz::utils::test_logger::StringBuffer;

use std::sync::{atomic, Arc};
{
let data: Vec<u8> = vec![0];
payer_proof_deser_test(&data, test_logger::DevNull {});
}
let mut threads = Vec::new();
let threads_running = Arc::new(atomic::AtomicUsize::new(0));
if let Ok(tests) = fs::read_dir("../test_cases/payer_proof_deser") {
for test in tests {
let mut data: Vec<u8> = Vec::new();
let path = test.unwrap().path();
fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
threads_running.fetch_add(1, atomic::Ordering::AcqRel);

let thread_count_ref = Arc::clone(&threads_running);
let main_thread_ref = std::thread::current();
threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
std::thread::spawn(move || {
let string_logger = StringBuffer::new();

let panic_logger = string_logger.clone();
let res = if ::std::panic::catch_unwind(move || {
payer_proof_deser_test(&data, panic_logger);
}).is_err() {
Some(string_logger.into_string())
} else { None };
thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
main_thread_ref.unpark();
res
})
));
while threads_running.load(atomic::Ordering::Acquire) > 32 {
std::thread::park();
}
}
}
let mut failed_outputs = Vec::new();
for (test, thread) in threads.drain(..) {
if let Some(output) = thread.join().unwrap() {
println!("\nOutput of {}:\n{}\n", test, output);
failed_outputs.push(test);
}
}
if !failed_outputs.is_empty() {
println!("Test cases which failed: ");
for case in failed_outputs {
println!("{}", case);
}
panic!();
}
}
1 change: 1 addition & 0 deletions fuzz/src/bin/gen_target.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ GEN_FAKE_HASHES_TEST onion_message
GEN_FAKE_HASHES_TEST peer_crypt
GEN_FAKE_HASHES_TEST process_network_graph
GEN_FAKE_HASHES_TEST process_onion_failure
GEN_FAKE_HASHES_TEST payer_proof_deser
GEN_FAKE_HASHES_TEST refund_deser
GEN_FAKE_HASHES_TEST router
GEN_FAKE_HASHES_TEST zbase32
Expand Down
1 change: 1 addition & 0 deletions fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod lsps_message;
pub mod offer_deser;
pub mod onion_hop_data;
pub mod onion_message;
pub mod payer_proof_deser;
pub mod peer_crypt;
pub mod process_network_graph;
pub mod process_onion_failure;
Expand Down
28 changes: 28 additions & 0 deletions fuzz/src/payer_proof_deser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.

use crate::utils::test_logger;
use core::convert::TryFrom;
use lightning::offers::payer_proof::PayerProof;

#[inline]
pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
if let Ok(payer_proof) = PayerProof::try_from(data.to_vec()) {
assert_eq!(data, payer_proof.bytes());
}
}

pub fn payer_proof_deser_test<Out: test_logger::Output>(data: &[u8], out: Out) {
do_test(data, out);
}

#[no_mangle]
pub extern "C" fn payer_proof_deser_run(data: *const u8, datalen: usize) {
do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull {});
}
1 change: 1 addition & 0 deletions fuzz/targets.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ void onion_message_run(const unsigned char* data, size_t data_len);
void peer_crypt_run(const unsigned char* data, size_t data_len);
void process_network_graph_run(const unsigned char* data, size_t data_len);
void process_onion_failure_run(const unsigned char* data, size_t data_len);
void payer_proof_deser_run(const unsigned char* data, size_t data_len);
void refund_deser_run(const unsigned char* data, size_t data_len);
void router_run(const unsigned char* data, size_t data_len);
void zbase32_run(const unsigned char* data, size_t data_len);
Expand Down
52 changes: 24 additions & 28 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ use crate::ln::outbound_payment::RecipientOnionFields;
use crate::ln::types::ChannelId;
use crate::offers::invoice::Bolt12Invoice;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::nonce::Nonce;
use crate::offers::payer_proof::Bolt12InvoiceType;
pub use crate::offers::payer_proof::PaidBolt12Invoice;
use crate::offers::static_invoice::StaticInvoice;
use crate::onion_message::messenger::Responder;
use crate::routing::gossip::NetworkUpdate;
Expand Down Expand Up @@ -1205,17 +1208,13 @@ pub enum Event {
///
/// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
fee_paid_msat: Option<u64>,
/// The BOLT 12 invoice that was paid. `None` if the payment was a non BOLT 12 payment.
/// The paid BOLT 12 invoice bundled with the data needed to construct a
/// [`PayerProof`], which selectively discloses invoice fields to prove payment to a
/// third party.
///
/// The BOLT 12 invoice is useful for proof of payment because it contains the
/// payment hash. A third party can verify that the payment was made by
/// showing the invoice and confirming that the payment hash matches
/// the hash of the payment preimage.
/// `None` for non-BOLT 12 payments.
///
/// However, the [`PaidBolt12Invoice`] can also be of type [`StaticInvoice`], which
/// is a special [`Bolt12Invoice`] where proof of payment is not possible.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`PayerProof`]: crate::offers::payer_proof::PayerProof
bolt12_invoice: Option<PaidBolt12Invoice>,
},
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
Expand Down Expand Up @@ -2107,13 +2106,16 @@ impl Writeable for Event {
ref bolt12_invoice,
} => {
2u8.write(writer)?;
let invoice_type = bolt12_invoice.as_ref().map(|paid| paid.invoice_type());
let payment_nonce = bolt12_invoice.as_ref().and_then(|paid| paid.nonce());
write_tlv_fields!(writer, {
(0, payment_preimage, required),
(1, payment_hash, required),
(3, payment_id, option),
(5, fee_paid_msat, option),
(7, amount_msat, option),
(9, bolt12_invoice, option),
(9, invoice_type, option),
(11, payment_nonce, option),
});
},
&Event::PaymentPathFailed {
Expand Down Expand Up @@ -2605,20 +2607,30 @@ impl MaybeReadable for Event {
let mut payment_id = None;
let mut amount_msat = None;
let mut fee_paid_msat = None;
let mut bolt12_invoice = None;
let mut invoice_type: Option<Bolt12InvoiceType> = None;
let mut payment_nonce: Option<Nonce> = None;
read_tlv_fields!(reader, {
(0, payment_preimage, required),
(1, payment_hash, option),
(3, payment_id, option),
(5, fee_paid_msat, option),
(7, amount_msat, option),
(9, bolt12_invoice, option),
(9, invoice_type, option),
(11, payment_nonce, option),
});
if payment_hash.is_none() {
payment_hash = Some(PaymentHash(
Sha256::hash(&payment_preimage.0[..]).to_byte_array(),
));
}
let bolt12_invoice = invoice_type.map(|mut invoice| {
// The nonce is serialized separately for backwards compatibility; re-bundle
// it into the invoice so payer proofs can be built from this event.
if let Bolt12InvoiceType::Bolt12Invoice { nonce, .. } = &mut invoice {
*nonce = payment_nonce;
}
PaidBolt12Invoice::new(invoice, payment_preimage)
});
Ok(Some(Event::PaymentSent {
payment_id,
payment_preimage,
Expand Down Expand Up @@ -3278,19 +3290,3 @@ impl<T: EventHandler> EventHandler for Arc<T> {
self.deref().handle_event(event)
}
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Bolt12Invoice(Bolt12Invoice),
/// The Static invoice, used in the async payment specification update proposal,
/// where the user cannot perform proof of payment.
StaticInvoice(StaticInvoice),
}

impl_writeable_tlv_based_enum!(PaidBolt12Invoice,
{0, Bolt12Invoice} => (),
{2, StaticInvoice} => (),
);
22 changes: 11 additions & 11 deletions lightning/src/ln/async_payments_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentTlvs};
use crate::blinded_path::payment::{DummyTlvs, PaymentContext};
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::events::{
Event, EventsProvider, HTLCHandlingFailureReason, HTLCHandlingFailureType, PaidBolt12Invoice,
Event, EventsProvider, HTLCHandlingFailureReason, HTLCHandlingFailureType,
PaymentFailureReason, PaymentPurpose,
};
use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters};
Expand Down Expand Up @@ -1000,7 +1000,7 @@ fn ignore_duplicate_invoice() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice.clone())));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));

// After paying the static invoice, check that regular invoice received from async recipient is ignored.
match sender.onion_messenger.peel_onion_message(&invoice_om) {
Expand Down Expand Up @@ -1085,7 +1085,7 @@ fn ignore_duplicate_invoice() {

// After paying invoice, check that static invoice is ignored.
let res = claim_payment(sender, route[0], payment_preimage);
assert_eq!(res, Some(PaidBolt12Invoice::Bolt12Invoice(invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.bolt12_invoice()), Some(&invoice));

sender.onion_messenger.handle_onion_message(always_online_node_id, &static_invoice_om);
let async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(sender.node);
Expand Down Expand Up @@ -1156,7 +1156,7 @@ fn async_receive_flow_success() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
}

#[test]
Expand Down Expand Up @@ -1238,7 +1238,7 @@ fn async_payment_delivers_payment_metadata() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.and_then(|paid| paid.static_invoice().cloned()), Some(static_invoice));
}

#[cfg_attr(feature = "std", ignore)]
Expand Down Expand Up @@ -2485,7 +2485,7 @@ fn refresh_static_invoices_for_used_offers() {
let claimable_ev = do_pass_along_path(args).unwrap();
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let res = claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res.0, Some(PaidBolt12Invoice::StaticInvoice(updated_invoice)));
assert_eq!(res.0.as_ref().and_then(|paid| paid.static_invoice()), Some(&updated_invoice));
}

#[cfg_attr(feature = "std", ignore)]
Expand Down Expand Up @@ -2820,7 +2820,7 @@ fn invoice_server_is_not_channel_peer() {
let claimable_ev = do_pass_along_path(args).unwrap();
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let res = claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res.0, Some(PaidBolt12Invoice::StaticInvoice(invoice)));
assert_eq!(res.0.as_ref().and_then(|paid| paid.static_invoice()), Some(&invoice));
}

#[test]
Expand Down Expand Up @@ -3063,7 +3063,7 @@ fn async_payment_e2e() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
}

#[test]
Expand Down Expand Up @@ -3303,7 +3303,7 @@ fn intercepted_hold_htlc() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
}

#[test]
Expand Down Expand Up @@ -3553,7 +3553,7 @@ fn release_htlc_races_htlc_onion_decode() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
}

#[test]
Expand Down Expand Up @@ -3717,5 +3717,5 @@ fn async_payment_e2e_release_before_hold_registered() {
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
}
Loading