Skip to content

Commit cfd962a

Browse files
committed
Attributable failures optional feature bit
Signal to senders that the node will return attributable failures. When the sender makes sure all path nodes supports this, they will be able to attribute every failure that may occur.
1 parent 55a2644 commit cfd962a

File tree

3 files changed

+39
-11
lines changed

3 files changed

+39
-11
lines changed

lightning-invoice/src/lib.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -597,12 +597,18 @@ impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False, tb::False, tb::F
597597
/// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
598598
/// `InvoiceBuilder::build(self)` becomes available.
599599
pub fn new(currency: Currency) -> Self {
600+
let mut features = Bolt11InvoiceFeatures::empty();
601+
features.set_attributable_failures_optional();
602+
603+
let mut tagged_fields = Vec::with_capacity(8);
604+
tagged_fields.push(TaggedField::Features(features));
605+
600606
InvoiceBuilder {
601607
currency,
602608
amount: None,
603609
si_prefix: None,
604610
timestamp: None,
605-
tagged_fields: Vec::with_capacity(8),
611+
tagged_fields,
606612
error: None,
607613

608614
phantom_d: core::marker::PhantomData,

lightning-types/src/features.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ mod sealed {
154154
// Byte 3
155155
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
156156
// Byte 4
157-
Quiescence | OnionMessages,
157+
Quiescence | OnionMessages | AttributableFailures,
158158
// Byte 5
159159
ProvideStorage | ChannelType | SCIDPrivacy,
160160
// Byte 6
@@ -175,7 +175,7 @@ mod sealed {
175175
// Byte 3
176176
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
177177
// Byte 4
178-
Quiescence | OnionMessages,
178+
Quiescence | OnionMessages | AttributableFailures,
179179
// Byte 5
180180
ProvideStorage | ChannelType | SCIDPrivacy,
181181
// Byte 6
@@ -199,7 +199,7 @@ mod sealed {
199199
// Byte 3
200200
,
201201
// Byte 4
202-
,
202+
AttributableFailures,
203203
// Byte 5
204204
,
205205
// Byte 6
@@ -219,7 +219,7 @@ mod sealed {
219219
// Byte 3
220220
,
221221
// Byte 4
222-
,
222+
AttributableFailures,
223223
// Byte 5
224224
,
225225
// Byte 6
@@ -548,6 +548,16 @@ mod sealed {
548548
supports_quiescence,
549549
requires_quiescence
550550
);
551+
define_feature!(
552+
37,
553+
AttributableFailures,
554+
[InitContext, NodeContext, Bolt11InvoiceContext, Bolt12InvoiceContext],
555+
"Feature flags for `option_attributable_failures`.",
556+
set_attributable_failures_optional,
557+
set_attributable_failures_required,
558+
supports_attributable_failures,
559+
requires_attributable_failures
560+
);
551561
define_feature!(
552562
39,
553563
OnionMessages,

lightning/src/offers/invoice.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -413,14 +413,17 @@ macro_rules! invoice_builder_methods {
413413
payment_paths: Vec<BlindedPaymentPath>, created_at: Duration,
414414
payment_hash: PaymentHash, amount_msats: u64, signing_pubkey: PublicKey,
415415
) -> InvoiceFields {
416+
let mut features = Bolt12InvoiceFeatures::empty();
417+
features.set_attributable_failures_optional();
418+
416419
InvoiceFields {
417420
payment_paths,
418421
created_at,
419422
relative_expiry: None,
420423
payment_hash,
421424
amount_msats,
422425
fallbacks: None,
423-
features: Bolt12InvoiceFeatures::empty(),
426+
features,
424427
signing_pubkey,
425428
#[cfg(test)]
426429
experimental_baz: None,
@@ -1777,6 +1780,7 @@ mod tests {
17771780
use bitcoin::secp256k1::{self, Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey};
17781781
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
17791782

1783+
use core::f32::consts::E;
17801784
use core::time::Duration;
17811785

17821786
use crate::blinded_path::message::BlindedMessagePath;
@@ -1878,7 +1882,10 @@ mod tests {
18781882
assert!(!unsigned_invoice.is_expired());
18791883
assert_eq!(unsigned_invoice.payment_hash(), payment_hash);
18801884
assert!(unsigned_invoice.fallbacks().is_empty());
1881-
assert_eq!(unsigned_invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
1885+
1886+
let mut expected_features = Bolt12InvoiceFeatures::empty();
1887+
expected_features.set_attributable_failures_optional();
1888+
assert_eq!(unsigned_invoice.invoice_features(), &expected_features);
18821889

18831890
match UnsignedBolt12Invoice::try_from(buffer) {
18841891
Err(e) => panic!("error parsing unsigned invoice: {:?}", e),
@@ -1926,7 +1933,7 @@ mod tests {
19261933
assert!(!invoice.is_expired());
19271934
assert_eq!(invoice.payment_hash(), payment_hash);
19281935
assert!(invoice.fallbacks().is_empty());
1929-
assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
1936+
assert_eq!(invoice.invoice_features(), &expected_features);
19301937
assert!(!invoice.is_for_refund_without_paths());
19311938

19321939
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
@@ -1974,7 +1981,7 @@ mod tests {
19741981
payment_hash: Some(&payment_hash),
19751982
amount: Some(1000),
19761983
fallbacks: None,
1977-
features: None,
1984+
features: Some(&expected_features),
19781985
node_id: Some(&recipient_pubkey()),
19791986
message_paths: None,
19801987
},
@@ -2009,6 +2016,9 @@ mod tests {
20092016
let mut buffer = Vec::new();
20102017
invoice.write(&mut buffer).unwrap();
20112018

2019+
let mut expected_features = Bolt12InvoiceFeatures::empty();
2020+
expected_features.set_attributable_failures_optional();
2021+
20122022
assert_eq!(invoice.bytes, buffer.as_slice());
20132023
assert_eq!(invoice.payer_metadata(), &[1; 32]);
20142024
assert_eq!(invoice.offer_chains(), None);
@@ -2034,7 +2044,7 @@ mod tests {
20342044
assert!(!invoice.is_expired());
20352045
assert_eq!(invoice.payment_hash(), payment_hash);
20362046
assert!(invoice.fallbacks().is_empty());
2037-
assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
2047+
assert_eq!(invoice.invoice_features(), &expected_features);
20382048
assert!(invoice.is_for_refund_without_paths());
20392049

20402050
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
@@ -2077,7 +2087,7 @@ mod tests {
20772087
payment_hash: Some(&payment_hash),
20782088
amount: Some(1000),
20792089
fallbacks: None,
2080-
features: None,
2090+
features: Some(&expected_features),
20812091
node_id: Some(&recipient_pubkey()),
20822092
message_paths: None,
20832093
},
@@ -2501,6 +2511,7 @@ mod tests {
25012511

25022512
let mut features = Bolt12InvoiceFeatures::empty();
25032513
features.set_basic_mpp_optional();
2514+
features.set_attributable_failures_optional();
25042515

25052516
let invoice = OfferBuilder::new(recipient_pubkey())
25062517
.amount_msats(1000)
@@ -2838,6 +2849,7 @@ mod tests {
28382849
Ok(invoice) => {
28392850
let mut features = Bolt12InvoiceFeatures::empty();
28402851
features.set_basic_mpp_optional();
2852+
features.set_attributable_failures_optional();
28412853
assert_eq!(invoice.invoice_features(), &features);
28422854
},
28432855
Err(e) => panic!("error parsing invoice: {:?}", e),

0 commit comments

Comments
 (0)