Skip to content

Commit 817ab5e

Browse files
authored
Merge pull request #4363 from jgmcalpine/invoice-raw-use-payment-hash
invoice: Use PaymentHash in raw invoice types
2 parents 65b6494 + d652d86 commit 817ab5e

9 files changed

Lines changed: 351 additions & 189 deletions

File tree

lightning-invoice/src/de.rs

Lines changed: 93 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::Bolt11Bech32;
1616
use bitcoin::hashes::sha256;
1717
use bitcoin::hashes::Hash;
1818
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
19-
use lightning_types::payment::PaymentSecret;
19+
use lightning_types::payment::{PaymentHash, PaymentSecret};
2020
use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
2121

2222
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
@@ -89,6 +89,18 @@ impl FromBase32 for PaymentSecret {
8989
}
9090
}
9191

92+
impl FromBase32 for PaymentHash {
93+
type Err = Bolt11ParseError;
94+
95+
fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
96+
if field_data.len() != 52 {
97+
return Err(Bolt11ParseError::InvalidSliceLength(field_data.len(), 52, "PaymentHash"));
98+
}
99+
let data_bytes = <[u8; 32]>::from_base32(field_data)?;
100+
Ok(PaymentHash(data_bytes))
101+
}
102+
}
103+
92104
impl FromBase32 for Bolt11InvoiceFeatures {
93105
type Err = Bolt11ParseError;
94106

@@ -540,7 +552,7 @@ impl FromBase32 for TaggedField {
540552

541553
match tag.to_u8() {
542554
constants::TAG_PAYMENT_HASH => {
543-
Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?))
555+
Ok(TaggedField::PaymentHash(PaymentHash::from_base32(field_data)?))
544556
},
545557
constants::TAG_DESCRIPTION => {
546558
Ok(TaggedField::Description(Description::from_base32(field_data)?))
@@ -1068,54 +1080,61 @@ mod test {
10681080
use crate::TaggedField::*;
10691081
use crate::{
10701082
Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1071-
RawHrp, Sha256, SiPrefix, SignedRawBolt11Invoice,
1083+
RawHrp, SiPrefix, SignedRawBolt11Invoice,
10721084
};
1085+
use bitcoin::hex::FromHex;
10731086
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
10741087
use lightning_types::features::Bolt11InvoiceFeatures;
10751088

10761089
// Feature bits 9, 15, and 99 are set.
10771090
let expected_features =
10781091
Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]);
10791092
let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu";
1080-
let invoice =
1081-
SignedRawBolt11Invoice {
1082-
raw_invoice: RawBolt11Invoice {
1083-
hrp: RawHrp {
1084-
currency: Currency::Bitcoin,
1085-
raw_amount: Some(25),
1086-
si_prefix: Some(SiPrefix::Milli),
1087-
},
1088-
data: RawDataPart {
1089-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1090-
tagged_fields: vec ! [
1091-
PaymentHash(Sha256(sha256::Hash::from_str(
1092-
"0001020304050607080900010203040506070809000102030405060708090102"
1093-
).unwrap())).into(),
1093+
let invoice = SignedRawBolt11Invoice {
1094+
raw_invoice: RawBolt11Invoice {
1095+
hrp: RawHrp {
1096+
currency: Currency::Bitcoin,
1097+
raw_amount: Some(25),
1098+
si_prefix: Some(SiPrefix::Milli),
1099+
},
1100+
data: RawDataPart {
1101+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1102+
tagged_fields: vec ! [
1103+
crate::TaggedField::PaymentHash(crate::PaymentHash(
1104+
<[u8; 32]>::try_from(
1105+
Vec::from_hex(
1106+
"0001020304050607080900010203040506070809000102030405060708090102",
1107+
)
1108+
.unwrap(),
1109+
)
1110+
.unwrap(),
1111+
))
1112+
.into(),
10941113
Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(),
10951114
PaymentSecret(crate::PaymentSecret([17; 32])).into(),
10961115
Features(expected_features).into()],
1097-
},
10981116
},
1099-
hash: [
1100-
0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32,
1101-
0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f,
1102-
0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9,
1103-
],
1104-
signature: Bolt11InvoiceSignature(
1105-
RecoverableSignature::from_compact(
1106-
&[
1107-
0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf,
1108-
0x68, 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64,
1109-
0xd3, 0x60, 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab,
1110-
0x4c, 0x85, 0xd3, 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08,
1111-
0x12, 0xf9, 0x5d, 0x97, 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda,
1112-
0xe0, 0x1a, 0xf3, 0xc1,
1113-
],
1114-
RecoveryId::from_i32(1).unwrap(),
1115-
)
1116-
.unwrap(),
1117-
),
1118-
};
1117+
},
1118+
hash: [
1119+
0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32, 0xec,
1120+
0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f, 0xf8, 0x7e,
1121+
0x44, 0x54, 0x55, 0xb9,
1122+
],
1123+
signature: Bolt11InvoiceSignature(
1124+
RecoverableSignature::from_compact(
1125+
&[
1126+
0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf,
1127+
0x68, 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64,
1128+
0xd3, 0x60, 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab,
1129+
0x4c, 0x85, 0xd3, 0x58, 0xea, 0x14, 0xd0, 0xae, 0x34, 0x2d, 0xa3, 0x08,
1130+
0x12, 0xf9, 0x5d, 0x97, 0x60, 0x82, 0xea, 0xac, 0x81, 0x39, 0x11, 0xda,
1131+
0xe0, 0x1a, 0xf3, 0xc1,
1132+
],
1133+
RecoveryId::from_i32(1).unwrap(),
1134+
)
1135+
.unwrap(),
1136+
),
1137+
};
11191138
assert_eq!(invoice_str, invoice.to_string());
11201139
assert_eq!(invoice_str.parse(), Ok(invoice));
11211140
}
@@ -1125,8 +1144,9 @@ mod test {
11251144
use crate::TaggedField::*;
11261145
use crate::{
11271146
Bolt11InvoiceSignature, Currency, PositiveTimestamp, RawBolt11Invoice, RawDataPart,
1128-
RawHrp, Sha256, SignedRawBolt11Invoice,
1147+
RawHrp, SignedRawBolt11Invoice,
11291148
};
1149+
use bitcoin::hex::FromHex;
11301150
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
11311151

11321152
assert_eq!(
@@ -1143,9 +1163,16 @@ mod test {
11431163
data: RawDataPart {
11441164
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
11451165
tagged_fields: vec ! [
1146-
PaymentHash(Sha256(sha256::Hash::from_str(
1147-
"0001020304050607080900010203040506070809000102030405060708090102"
1148-
).unwrap())).into(),
1166+
crate::TaggedField::PaymentHash(crate::PaymentHash(
1167+
<[u8; 32]>::try_from(
1168+
Vec::from_hex(
1169+
"0001020304050607080900010203040506070809000102030405060708090102",
1170+
)
1171+
.unwrap(),
1172+
)
1173+
.unwrap(),
1174+
))
1175+
.into(),
11491176
Description(
11501177
crate::Description::new(
11511178
"Please consider supporting this project".to_owned()
@@ -1289,9 +1316,10 @@ mod test {
12891316
use crate::TaggedField::*;
12901317
use crate::{
12911318
Bolt11Invoice, Bolt11InvoiceFeatures, Bolt11InvoiceSignature, Currency,
1292-
PositiveTimestamp, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, Sha256,
1319+
PositiveTimestamp, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField,
12931320
SignedRawBolt11Invoice,
12941321
};
1322+
use bitcoin::hex::FromHex;
12951323
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
12961324
use bitcoin::secp256k1::PublicKey;
12971325
use lightning_types::routing::{RouteHint, RouteHintHop, RoutingFees};
@@ -1310,10 +1338,13 @@ mod test {
13101338
}
13111339

13121340
// Invoice fields
1313-
let payment_hash = sha256::Hash::from_str(
1314-
"0001020304050607080900010203040506070809000102030405060708090102",
1315-
)
1316-
.unwrap();
1341+
let payment_hash = crate::PaymentHash(
1342+
<[u8; 32]>::try_from(
1343+
Vec::from_hex("0001020304050607080900010203040506070809000102030405060708090102")
1344+
.unwrap(),
1345+
)
1346+
.unwrap(),
1347+
);
13171348
let description = "A".repeat(639);
13181349
let fallback_addr = crate::Fallback::SegWitProgram {
13191350
version: bitcoin::WitnessVersion::V0,
@@ -1346,7 +1377,7 @@ mod test {
13461377
data: RawDataPart {
13471378
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
13481379
tagged_fields: vec![
1349-
PaymentHash(Sha256(payment_hash)).into(),
1380+
crate::TaggedField::PaymentHash(payment_hash).into(),
13501381
Description(crate::Description::new(description).unwrap()).into(),
13511382
PayeePubKey(crate::PayeePubKey(payee_pk)).into(),
13521383
ExpiryTime(crate::ExpiryTime(std::time::Duration::from_secs(u64::MAX))).into(),
@@ -1414,4 +1445,18 @@ mod test {
14141445
assert!(parse_is_code_length_err(&too_long));
14151446
assert!(!parse_is_code_length_err(&too_long[..too_long.len() - 1]));
14161447
}
1448+
1449+
#[test]
1450+
fn test_payment_hash_from_base32_invalid_len() {
1451+
use crate::PaymentHash;
1452+
1453+
// PaymentHash must be 52 base32 characters (32 bytes).
1454+
// Test with 51 characters (too short).
1455+
let input = vec![Fe32::try_from(0).unwrap(); 51];
1456+
assert!(PaymentHash::from_base32(&input).is_err());
1457+
1458+
// Test with 53 characters (too long).
1459+
let input = vec![Fe32::try_from(0).unwrap(); 53];
1460+
assert!(PaymentHash::from_base32(&input).is_err());
1461+
}
14171462
}

0 commit comments

Comments
 (0)