Skip to content

Commit 1ceb41e

Browse files
authored
Merge pull request #2023 from futurepaul/fallback-to-address
add `Fallback` getter that returns `Address`
2 parents f30dc85 + cf0a90b commit 1ceb41e

File tree

5 files changed

+67
-32
lines changed

5 files changed

+67
-32
lines changed

lightning-invoice/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ num-traits = { version = "0.2.8", default-features = false }
2727
bitcoin_hashes = { version = "0.11", default-features = false }
2828
hashbrown = { version = "0.8", optional = true }
2929
serde = { version = "1.0.118", optional = true }
30+
bitcoin = { version = "0.29.0", default-features = false }
3031

3132
[dev-dependencies]
3233
lightning = { version = "0.0.114", path = "../lightning", default-features = false, features = ["_test_utils"] }

lightning-invoice/src/de.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[cfg(feature = "std")]
22
use std::error;
3+
use core::convert::TryFrom;
34
use core::fmt;
45
use core::fmt::{Display, Formatter};
56
use core::num::ParseIntError;
@@ -8,6 +9,8 @@ use core::str::FromStr;
89

910
use bech32::{u5, FromBase32};
1011

12+
use bitcoin::{PubkeyHash, ScriptHash};
13+
use bitcoin::util::address::WitnessVersion;
1114
use bitcoin_hashes::Hash;
1215
use bitcoin_hashes::sha256;
1316
use crate::prelude::*;
@@ -550,27 +553,24 @@ impl FromBase32 for Fallback {
550553
if bytes.len() < 2 || bytes.len() > 40 {
551554
return Err(ParseError::InvalidSegWitProgramLength);
552555
}
553-
556+
let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
554557
Ok(Fallback::SegWitProgram {
555558
version,
556559
program: bytes
557560
})
558561
},
559562
17 => {
560-
if bytes.len() != 20 {
561-
return Err(ParseError::InvalidPubKeyHashLength);
562-
}
563-
//TODO: refactor once const generics are available
564-
let mut pkh = [0u8; 20];
565-
pkh.copy_from_slice(&bytes);
563+
let pkh = match PubkeyHash::from_slice(&bytes) {
564+
Ok(pkh) => pkh,
565+
Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidPubKeyHashLength),
566+
};
566567
Ok(Fallback::PubKeyHash(pkh))
567568
}
568569
18 => {
569-
if bytes.len() != 20 {
570-
return Err(ParseError::InvalidScriptHashLength);
571-
}
572-
let mut sh = [0u8; 20];
573-
sh.copy_from_slice(&bytes);
570+
let sh = match ScriptHash::from_slice(&bytes) {
571+
Ok(sh) => sh,
572+
Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidScriptHashLength),
573+
};
574574
Ok(Fallback::ScriptHash(sh))
575575
}
576576
_ => Err(ParseError::Skip)
@@ -852,26 +852,29 @@ mod test {
852852
fn test_parse_fallback() {
853853
use crate::Fallback;
854854
use bech32::FromBase32;
855+
use bitcoin::{PubkeyHash, ScriptHash};
856+
use bitcoin::util::address::WitnessVersion;
857+
use bitcoin_hashes::Hash;
855858

856859
let cases = vec![
857860
(
858861
from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
859-
Ok(Fallback::PubKeyHash([
862+
Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
860863
0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
861864
0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
862-
]))
865+
]).unwrap()))
863866
),
864867
(
865868
from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
866-
Ok(Fallback::ScriptHash([
869+
Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
867870
0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
868871
0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
869-
]))
872+
]).unwrap()))
870873
),
871874
(
872875
from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
873876
Ok(Fallback::SegWitProgram {
874-
version: u5::try_from_u8(0).unwrap(),
877+
version: WitnessVersion::V0,
875878
program: Vec::from(&[
876879
0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
877880
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6

lightning-invoice/src/lib.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ extern crate serde;
4747
use std::time::SystemTime;
4848

4949
use bech32::u5;
50-
use bitcoin_hashes::Hash;
51-
use bitcoin_hashes::sha256;
50+
use bitcoin::{Address, Network, PubkeyHash, ScriptHash};
51+
use bitcoin::util::address::{Payload, WitnessVersion};
52+
use bitcoin_hashes::{Hash, sha256};
5253
use lightning::ln::PaymentSecret;
5354
use lightning::ln::features::InvoiceFeatures;
5455
#[cfg(any(doc, test))]
@@ -446,17 +447,16 @@ pub struct ExpiryTime(Duration);
446447
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
447448
pub struct MinFinalCltvExpiryDelta(pub u64);
448449

449-
// TODO: better types instead onf byte arrays
450450
/// Fallback address in case no LN payment is possible
451451
#[allow(missing_docs)]
452452
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
453453
pub enum Fallback {
454454
SegWitProgram {
455-
version: u5,
455+
version: WitnessVersion,
456456
program: Vec<u8>,
457457
},
458-
PubKeyHash([u8; 20]),
459-
ScriptHash([u8; 20]),
458+
PubKeyHash(PubkeyHash),
459+
ScriptHash(ScriptHash),
460460
}
461461

462462
/// Recoverable signature
@@ -1300,6 +1300,33 @@ impl Invoice {
13001300
self.signed_invoice.fallbacks()
13011301
}
13021302

1303+
/// Returns a list of all fallback addresses as [`Address`]es
1304+
pub fn fallback_addresses(&self) -> Vec<Address> {
1305+
self.fallbacks().iter().map(|fallback| {
1306+
let network = match self.currency() {
1307+
Currency::Bitcoin => Network::Bitcoin,
1308+
Currency::BitcoinTestnet => Network::Testnet,
1309+
Currency::Regtest => Network::Regtest,
1310+
Currency::Simnet => Network::Regtest,
1311+
Currency::Signet => Network::Signet,
1312+
};
1313+
1314+
let payload = match fallback {
1315+
Fallback::SegWitProgram { version, program } => {
1316+
Payload::WitnessProgram { version: *version, program: program.to_vec() }
1317+
}
1318+
Fallback::PubKeyHash(pkh) => {
1319+
Payload::PubkeyHash(*pkh)
1320+
}
1321+
Fallback::ScriptHash(sh) => {
1322+
Payload::ScriptHash(*sh)
1323+
}
1324+
};
1325+
1326+
Address { payload, network }
1327+
}).collect()
1328+
}
1329+
13031330
/// Returns a list of all routes included in the invoice
13041331
pub fn private_routes(&self) -> Vec<&PrivateRoute> {
13051332
self.signed_invoice.private_routes()
@@ -1609,6 +1636,7 @@ impl<'de> Deserialize<'de> for Invoice {
16091636

16101637
#[cfg(test)]
16111638
mod test {
1639+
use bitcoin::Script;
16121640
use bitcoin_hashes::hex::FromHex;
16131641
use bitcoin_hashes::sha256;
16141642

@@ -1972,7 +2000,7 @@ mod test {
19722000
.payee_pub_key(public_key)
19732001
.expiry_time(Duration::from_secs(54321))
19742002
.min_final_cltv_expiry_delta(144)
1975-
.fallback(Fallback::PubKeyHash([0;20]))
2003+
.fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap()))
19762004
.private_route(route_1.clone())
19772005
.private_route(route_2.clone())
19782006
.description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
@@ -1998,7 +2026,9 @@ mod test {
19982026
assert_eq!(invoice.payee_pub_key(), Some(&public_key));
19992027
assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
20002028
assert_eq!(invoice.min_final_cltv_expiry_delta(), 144);
2001-
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
2029+
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap())]);
2030+
let address = Address::from_script(&Script::new_p2pkh(&PubkeyHash::from_slice(&[0;20]).unwrap()), Network::Testnet).unwrap();
2031+
assert_eq!(invoice.fallback_addresses(), vec![address]);
20022032
assert_eq!(invoice.private_routes(), vec![&PrivateRoute(route_1), &PrivateRoute(route_2)]);
20032033
assert_eq!(
20042034
invoice.description(),

lightning-invoice/src/ser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ impl ToBase32 for Fallback {
329329
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
330330
match *self {
331331
Fallback::SegWitProgram {version: v, program: ref p} => {
332-
writer.write_u5(v)?;
332+
writer.write_u5(Into::<u5>::into(v))?;
333333
p.write_base32(writer)
334334
},
335335
Fallback::PubKeyHash(ref hash) => {

lightning-invoice/tests/ser_de.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ extern crate lightning_invoice;
55
extern crate secp256k1;
66
extern crate hex;
77

8+
use bitcoin::util::address::WitnessVersion;
9+
use bitcoin::{PubkeyHash, ScriptHash};
810
use bitcoin_hashes::hex::FromHex;
911
use bitcoin_hashes::{sha256, Hash};
10-
use bech32::u5;
1112
use lightning::ln::PaymentSecret;
1213
use lightning::routing::gossip::RoutingFees;
1314
use lightning::routing::router::{RouteHint, RouteHintHop};
@@ -115,7 +116,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
115116
.payment_hash(sha256::Hash::from_hex(
116117
"0001020304050607080900010203040506070809000102030405060708090102"
117118
).unwrap())
118-
.fallback(Fallback::PubKeyHash([49, 114, 181, 101, 79, 102, 131, 200, 251, 20, 105, 89, 211, 71, 206, 48, 60, 174, 76, 167]))
119+
.fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[49, 114, 181, 101, 79, 102, 131, 200, 251, 20, 105, 89, 211, 71, 206, 48, 60, 174, 76, 167]).unwrap()))
119120
.build_raw()
120121
.unwrap()
121122
.sign(|_| {
@@ -137,7 +138,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
137138
.payment_hash(sha256::Hash::from_hex(
138139
"0001020304050607080900010203040506070809000102030405060708090102"
139140
).unwrap())
140-
.fallback(Fallback::PubKeyHash([4, 182, 31, 125, 193, 234, 13, 201, 148, 36, 70, 76, 196, 6, 77, 197, 100, 217, 30, 137]))
141+
.fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[4, 182, 31, 125, 193, 234, 13, 201, 148, 36, 70, 76, 196, 6, 77, 197, 100, 217, 30, 137]).unwrap()))
141142
.private_route(RouteHint(vec![RouteHintHop {
142143
src_node_id: PublicKey::from_slice(&hex::decode(
143144
"029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"
@@ -176,7 +177,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
176177
.payment_hash(sha256::Hash::from_hex(
177178
"0001020304050607080900010203040506070809000102030405060708090102"
178179
).unwrap())
179-
.fallback(Fallback::ScriptHash([143, 85, 86, 59, 154, 25, 243, 33, 194, 17, 233, 185, 243, 140, 223, 104, 110, 160, 120, 69]))
180+
.fallback(Fallback::ScriptHash(ScriptHash::from_slice(&[143, 85, 86, 59, 154, 25, 243, 33, 194, 17, 233, 185, 243, 140, 223, 104, 110, 160, 120, 69]).unwrap()))
180181
.build_raw()
181182
.unwrap()
182183
.sign(|_| {
@@ -198,7 +199,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
198199
.payment_hash(sha256::Hash::from_hex(
199200
"0001020304050607080900010203040506070809000102030405060708090102"
200201
).unwrap())
201-
.fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
202+
.fallback(Fallback::SegWitProgram { version: WitnessVersion::V0,
202203
program: vec![117, 30, 118, 232, 25, 145, 150, 212, 84, 148, 28, 69, 209, 179, 163, 35, 241, 67, 59, 214]
203204
})
204205
.build_raw()
@@ -222,7 +223,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
222223
.payment_hash(sha256::Hash::from_hex(
223224
"0001020304050607080900010203040506070809000102030405060708090102"
224225
).unwrap())
225-
.fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
226+
.fallback(Fallback::SegWitProgram { version: WitnessVersion::V0,
226227
program: vec![24, 99, 20, 60, 20, 197, 22, 104, 4, 189, 25, 32, 51, 86, 218, 19, 108, 152, 86, 120, 205, 77, 39, 161, 184, 198, 50, 150, 4, 144, 50, 98]
227228
})
228229
.build_raw()

0 commit comments

Comments
 (0)