Skip to content

Commit 2bc5fd4

Browse files
BOLT 12 invoice: expose common helper methods and fields
Useful for static invoice support.
1 parent 4b79cd5 commit 2bc5fd4

File tree

1 file changed

+68
-52
lines changed

1 file changed

+68
-52
lines changed

lightning/src/offers/invoice.rs

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ pub struct ExplicitSigningPubkey {}
200200
/// [`Bolt12Invoice::signing_pubkey`] was derived.
201201
///
202202
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
203-
pub struct DerivedSigningPubkey(KeyPair);
203+
pub struct DerivedSigningPubkey(pub(super) KeyPair);
204204

205205
impl SigningPubkeyStrategy for ExplicitSigningPubkey {}
206206
impl SigningPubkeyStrategy for DerivedSigningPubkey {}
@@ -958,14 +958,7 @@ impl InvoiceContents {
958958

959959
#[cfg(feature = "std")]
960960
fn is_expired(&self) -> bool {
961-
let absolute_expiry = self.created_at().checked_add(self.relative_expiry());
962-
match absolute_expiry {
963-
Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
964-
Ok(elapsed) => elapsed > seconds_from_epoch,
965-
Err(_) => false,
966-
},
967-
None => false,
968-
}
961+
is_expired(self.created_at(), self.relative_expiry())
969962
}
970963

971964
fn payment_hash(&self) -> PaymentHash {
@@ -977,36 +970,9 @@ impl InvoiceContents {
977970
}
978971

979972
fn fallbacks(&self) -> Vec<Address> {
980-
let chain = self.chain();
981-
let network = if chain == ChainHash::using_genesis_block(Network::Bitcoin) {
982-
Network::Bitcoin
983-
} else if chain == ChainHash::using_genesis_block(Network::Testnet) {
984-
Network::Testnet
985-
} else if chain == ChainHash::using_genesis_block(Network::Signet) {
986-
Network::Signet
987-
} else if chain == ChainHash::using_genesis_block(Network::Regtest) {
988-
Network::Regtest
989-
} else {
990-
return Vec::new()
991-
};
992-
993-
let to_valid_address = |address: &FallbackAddress| {
994-
let version = match WitnessVersion::try_from(address.version) {
995-
Ok(version) => version,
996-
Err(_) => return None,
997-
};
998-
999-
let program = &address.program;
1000-
let witness_program = match WitnessProgram::new(version, program.clone()) {
1001-
Ok(witness_program) => witness_program,
1002-
Err(_) => return None,
1003-
};
1004-
Some(Address::new(network, Payload::WitnessProgram(witness_program)))
1005-
};
1006-
1007973
self.fields().fallbacks
1008974
.as_ref()
1009-
.map(|fallbacks| fallbacks.iter().filter_map(to_valid_address).collect())
975+
.map(|fallbacks| filter_fallbacks(self.chain(), fallbacks))
1010976
.unwrap_or_else(Vec::new)
1011977
}
1012978

@@ -1075,6 +1041,50 @@ impl InvoiceContents {
10751041
}
10761042
}
10771043

1044+
#[cfg(feature = "std")]
1045+
pub(super) fn is_expired(created_at: Duration, relative_expiry: Duration) -> bool {
1046+
let absolute_expiry = created_at.checked_add(relative_expiry);
1047+
match absolute_expiry {
1048+
Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
1049+
Ok(elapsed) => elapsed > seconds_from_epoch,
1050+
Err(_) => false,
1051+
},
1052+
None => false,
1053+
}
1054+
}
1055+
1056+
pub(super) fn filter_fallbacks(
1057+
chain: ChainHash, fallbacks: &Vec<FallbackAddress>
1058+
) -> Vec<Address> {
1059+
let network = if chain == ChainHash::using_genesis_block(Network::Bitcoin) {
1060+
Network::Bitcoin
1061+
} else if chain == ChainHash::using_genesis_block(Network::Testnet) {
1062+
Network::Testnet
1063+
} else if chain == ChainHash::using_genesis_block(Network::Signet) {
1064+
Network::Signet
1065+
} else if chain == ChainHash::using_genesis_block(Network::Regtest) {
1066+
Network::Regtest
1067+
} else {
1068+
return Vec::new()
1069+
};
1070+
1071+
let to_valid_address = |address: &FallbackAddress| {
1072+
let version = match WitnessVersion::try_from(address.version) {
1073+
Ok(version) => version,
1074+
Err(_) => return None,
1075+
};
1076+
1077+
let program = &address.program;
1078+
let witness_program = match WitnessProgram::new(version, program.clone()) {
1079+
Ok(witness_program) => witness_program,
1080+
Err(_) => return None,
1081+
};
1082+
Some(Address::new(network, Payload::WitnessProgram(witness_program)))
1083+
};
1084+
1085+
fallbacks.iter().filter_map(to_valid_address).collect()
1086+
}
1087+
10781088
impl InvoiceFields {
10791089
fn as_tlv_stream(&self) -> InvoiceTlvStreamRef {
10801090
let features = {
@@ -1154,12 +1164,12 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef, 160..240, {
11541164
(176, node_id: PublicKey),
11551165
});
11561166

1157-
type BlindedPathIter<'a> = core::iter::Map<
1167+
pub(super) type BlindedPathIter<'a> = core::iter::Map<
11581168
core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>,
11591169
for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPath,
11601170
>;
11611171

1162-
type BlindedPayInfoIter<'a> = core::iter::Map<
1172+
pub(super) type BlindedPayInfoIter<'a> = core::iter::Map<
11631173
core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>,
11641174
for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPayInfo,
11651175
>;
@@ -1205,8 +1215,8 @@ impl_writeable!(BlindedPayInfo, {
12051215
/// Wire representation for an on-chain fallback address.
12061216
#[derive(Clone, Debug, PartialEq)]
12071217
pub(super) struct FallbackAddress {
1208-
version: u8,
1209-
program: Vec<u8>,
1218+
pub(super) version: u8,
1219+
pub(super) program: Vec<u8>,
12101220
}
12111221

12121222
impl_writeable!(FallbackAddress, { version, program });
@@ -1294,17 +1304,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
12941304
},
12951305
) = tlv_stream;
12961306

1297-
let payment_paths = match (blindedpay, paths) {
1298-
(_, None) => return Err(Bolt12SemanticError::MissingPaths),
1299-
(None, _) => return Err(Bolt12SemanticError::InvalidPayInfo),
1300-
(_, Some(paths)) if paths.is_empty() => return Err(Bolt12SemanticError::MissingPaths),
1301-
(Some(blindedpay), Some(paths)) if paths.len() != blindedpay.len() => {
1302-
return Err(Bolt12SemanticError::InvalidPayInfo);
1303-
},
1304-
(Some(blindedpay), Some(paths)) => {
1305-
blindedpay.into_iter().zip(paths.into_iter()).collect::<Vec<_>>()
1306-
},
1307-
};
1307+
let payment_paths = construct_payment_paths(blindedpay, paths)?;
13081308

13091309
let created_at = match created_at {
13101310
None => return Err(Bolt12SemanticError::MissingCreationTime),
@@ -1372,6 +1372,22 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
13721372
}
13731373
}
13741374

1375+
pub(super) fn construct_payment_paths(
1376+
blinded_payinfos: Option<Vec<BlindedPayInfo>>, blinded_paths: Option<Vec<BlindedPath>>
1377+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, Bolt12SemanticError> {
1378+
match (blinded_payinfos, blinded_paths) {
1379+
(_, None) => Err(Bolt12SemanticError::MissingPaths),
1380+
(None, _) => Err(Bolt12SemanticError::InvalidPayInfo),
1381+
(_, Some(paths)) if paths.is_empty() => Err(Bolt12SemanticError::MissingPaths),
1382+
(Some(blindedpay), Some(paths)) if paths.len() != blindedpay.len() => {
1383+
Err(Bolt12SemanticError::InvalidPayInfo)
1384+
},
1385+
(Some(blindedpay), Some(paths)) => {
1386+
Ok(blindedpay.into_iter().zip(paths.into_iter()).collect::<Vec<_>>())
1387+
},
1388+
}
1389+
}
1390+
13751391
#[cfg(test)]
13761392
mod tests {
13771393
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};

0 commit comments

Comments
 (0)