Skip to content

Commit 41b267a

Browse files
shaavancodex
andcommitted
[test] Cover currency-denominated offer invoice responses
Add tests covering invoice request and invoice response handling for currency-denominated offers. This combines coverage for the standard flow that derives the final invoice amount through currency conversion and the insufficient-msat request path that must be rejected while building the invoice response. The merged test coverage exercises both the positive and deferred- validation paths for currency-denominated invoice responses. AI-assisted: Planning and writing the tests Co-Authored-By: OpenAI Codex <codex@openai.com>
1 parent 811a0d3 commit 41b267a

1 file changed

Lines changed: 126 additions & 2 deletions

File tree

lightning/src/offers/invoice_request.rs

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,10 +1568,12 @@ impl Readable for InvoiceRequestFields {
15681568
mod tests {
15691569
use super::{
15701570
ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequest, InvoiceRequestFields,
1571-
InvoiceRequestTlvStreamRef, UnsignedInvoiceRequest, EXPERIMENTAL_INVOICE_REQUEST_TYPES,
1572-
INVOICE_REQUEST_TYPES, PAYER_NOTE_LIMIT, SIGNATURE_TAG,
1571+
InvoiceRequestTlvStreamRef, InvoiceRequestVerifiedFromOffer, UnsignedInvoiceRequest,
1572+
EXPERIMENTAL_INVOICE_REQUEST_TYPES, INVOICE_REQUEST_TYPES, PAYER_NOTE_LIMIT, SIGNATURE_TAG,
15731573
};
15741574

1575+
use crate::blinded_path::message::BlindedMessagePath;
1576+
use crate::blinded_path::BlindedHop;
15751577
use crate::ln::channelmanager::PaymentId;
15761578
use crate::ln::inbound_payment::ExpandedKey;
15771579
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
@@ -1593,6 +1595,7 @@ mod tests {
15931595
use crate::types::features::{InvoiceRequestFeatures, OfferFeatures};
15941596
use crate::types::string::{PrintableString, UntrustedString};
15951597
use crate::util::ser::{BigSize, Readable, Writeable};
1598+
use crate::util::test_utils::TestCurrencyConversion;
15961599
use bitcoin::constants::ChainHash;
15971600
use bitcoin::network::Network;
15981601
use bitcoin::secp256k1::{self, Keypair, Secp256k1, SecretKey};
@@ -2346,6 +2349,127 @@ mod tests {
23462349
}
23472350
}
23482351

2352+
#[test]
2353+
fn responds_to_invoice_request_using_currency_conversion() {
2354+
let expanded_key = ExpandedKey::new([42; 32]);
2355+
let entropy = FixedEntropy {};
2356+
let nonce = Nonce::from_entropy_source(&entropy);
2357+
let secp_ctx = Secp256k1::new();
2358+
let payment_id = PaymentId([1; 32]);
2359+
let converter = TestCurrencyConversion {};
2360+
2361+
let invoice = OfferBuilder::new(recipient_pubkey(), &converter)
2362+
.amount(Amount::Currency {
2363+
iso4217_code: CurrencyCode::new(*b"USD").unwrap(),
2364+
amount: 10,
2365+
})
2366+
.build()
2367+
.unwrap()
2368+
.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
2369+
.unwrap()
2370+
.build_and_sign()
2371+
.unwrap()
2372+
.respond_with_no_std(&converter, payment_paths(), payment_hash(), now())
2373+
.unwrap()
2374+
.build()
2375+
.unwrap()
2376+
.sign(recipient_sign)
2377+
.unwrap();
2378+
2379+
assert_eq!(invoice.amount_msats(), 10_000);
2380+
assert_eq!(
2381+
invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx),
2382+
Ok(payment_id),
2383+
);
2384+
}
2385+
2386+
#[test]
2387+
fn fails_responding_to_invoice_request_with_insufficient_amount_for_currency_offer() {
2388+
let expanded_key = ExpandedKey::new([42; 32]);
2389+
let entropy = FixedEntropy {};
2390+
let nonce = Nonce::from_entropy_source(&entropy);
2391+
let secp_ctx = Secp256k1::new();
2392+
let payment_id = PaymentId([1; 32]);
2393+
let converter = TestCurrencyConversion {};
2394+
2395+
match OfferBuilder::new(recipient_pubkey(), &converter)
2396+
.amount(Amount::Currency {
2397+
iso4217_code: CurrencyCode::new(*b"USD").unwrap(),
2398+
amount: 10,
2399+
})
2400+
.build()
2401+
.unwrap()
2402+
.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
2403+
.unwrap()
2404+
.amount_msats(9_999)
2405+
.unwrap()
2406+
.build_and_sign()
2407+
.unwrap()
2408+
.respond_with_no_std(&converter, payment_paths(), payment_hash(), now())
2409+
{
2410+
Ok(_) => panic!("expected error"),
2411+
Err(e) => assert_eq!(e, Bolt12SemanticError::InsufficientAmount),
2412+
}
2413+
}
2414+
2415+
#[test]
2416+
fn responds_to_invoice_request_using_derived_keys_with_currency_conversion() {
2417+
let expanded_key = ExpandedKey::new([42; 32]);
2418+
let entropy = FixedEntropy {};
2419+
let nonce = Nonce::from_entropy_source(&entropy);
2420+
let secp_ctx = Secp256k1::new();
2421+
let payment_id = PaymentId([1; 32]);
2422+
let converter = TestCurrencyConversion {};
2423+
let blinded_path = BlindedMessagePath::from_blinded_path(
2424+
pubkey(40),
2425+
pubkey(41),
2426+
vec![
2427+
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
2428+
BlindedHop { blinded_node_id: recipient_pubkey(), encrypted_payload: vec![0; 44] },
2429+
],
2430+
);
2431+
2432+
let offer = OfferBuilder::deriving_signing_pubkey(
2433+
recipient_pubkey(),
2434+
&expanded_key,
2435+
nonce,
2436+
&converter,
2437+
&secp_ctx,
2438+
)
2439+
.amount(Amount::Currency { iso4217_code: CurrencyCode::new(*b"USD").unwrap(), amount: 10 })
2440+
.path(blinded_path)
2441+
.build()
2442+
.unwrap();
2443+
2444+
let invoice_request = offer
2445+
.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
2446+
.unwrap()
2447+
.build_and_sign()
2448+
.unwrap();
2449+
let verified_invoice_request =
2450+
invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).unwrap();
2451+
2452+
let invoice = match verified_invoice_request {
2453+
InvoiceRequestVerifiedFromOffer::DerivedKeys(request) => request
2454+
.respond_using_derived_keys_no_std(
2455+
&converter,
2456+
payment_paths(),
2457+
payment_hash(),
2458+
now(),
2459+
)
2460+
.unwrap()
2461+
.build_and_sign(&secp_ctx)
2462+
.unwrap(),
2463+
InvoiceRequestVerifiedFromOffer::ExplicitKeys(_) => panic!("expected derived keys"),
2464+
};
2465+
2466+
assert_eq!(invoice.amount_msats(), 10_000);
2467+
assert_eq!(
2468+
invoice.verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx),
2469+
Ok(payment_id),
2470+
);
2471+
}
2472+
23492473
#[test]
23502474
fn parses_invoice_request_with_metadata() {
23512475
let expanded_key = ExpandedKey::new([42; 32]);

0 commit comments

Comments
 (0)