Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
53 changes: 25 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::Bolt12InvoiceTypeWire;
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,17 @@ impl Writeable for Event {
ref bolt12_invoice,
} => {
2u8.write(writer)?;
let invoice_type =
bolt12_invoice.as_ref().map(|paid| paid.invoice_type().as_wire());
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 +2608,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<Bolt12InvoiceTypeWire> = 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(|invoice| {
// The nonce is serialized separately for backwards compatibility; re-bundle
// it into the invoice so payer proofs can be built from this event.
PaidBolt12Invoice::new(
invoice.into_invoice_type(payment_nonce),
payment_preimage,
)
});
Ok(Some(Event::PaymentSent {
payment_id,
payment_preimage,
Expand Down Expand Up @@ -3278,19 +3291,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