@@ -16,7 +16,7 @@ use crate::Bolt11Bech32;
1616use bitcoin:: hashes:: sha256;
1717use bitcoin:: hashes:: Hash ;
1818use bitcoin:: { PubkeyHash , ScriptHash , WitnessVersion } ;
19- use lightning_types:: payment:: PaymentSecret ;
19+ use lightning_types:: payment:: { PaymentHash , PaymentSecret } ;
2020use lightning_types:: routing:: { RouteHint , RouteHintHop , RoutingFees } ;
2121
2222use 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+
92104impl 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