Skip to content

Commit a6b469a

Browse files
committed
use bitcoin types in Fallback
1 parent 4174883 commit a6b469a

File tree

5 files changed

+84
-86
lines changed

5 files changed

+84
-86
lines changed

lightning-invoice/src/de.rs

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ use core::fmt::{Display, Formatter};
55
use core::num::ParseIntError;
66
use core::str;
77
use core::str::FromStr;
8+
use core::convert::TryFrom;
89

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

13+
use bitcoin::{PubkeyHash, ScriptHash};
14+
use bitcoin::util::address::WitnessVersion;
1215
use bitcoin_hashes::Hash;
1316
use bitcoin_hashes::sha256;
1417
use crate::prelude::*;
@@ -553,6 +556,11 @@ impl FromBase32 for Fallback {
553556
return Err(ParseError::InvalidSegWitProgramLength);
554557
}
555558

559+
let version = match WitnessVersion::try_from(version) {
560+
Ok(version) => version,
561+
Err(_) => return Err(ParseError::InvalidSegWitVersion),
562+
};
563+
556564
Ok(Fallback::SegWitProgram {
557565
version: version,
558566
program: bytes
@@ -562,17 +570,22 @@ impl FromBase32 for Fallback {
562570
if bytes.len() != 20 {
563571
return Err(ParseError::InvalidPubKeyHashLength);
564572
}
565-
//TODO: refactor once const generics are available
566-
let mut pkh = [0u8; 20];
567-
pkh.copy_from_slice(&bytes);
573+
let pkh = match PubkeyHash::from_slice(&bytes) {
574+
Ok(pkh) => pkh,
575+
Err(_) => return Err(ParseError::InvalidPubKeyHash),
576+
};
577+
568578
Ok(Fallback::PubKeyHash(pkh))
569579
}
570580
18 => {
571581
if bytes.len() != 20 {
572582
return Err(ParseError::InvalidScriptHashLength);
573583
}
574-
let mut sh = [0u8; 20];
575-
sh.copy_from_slice(&bytes);
584+
let sh = match ScriptHash::from_slice(&bytes) {
585+
Ok(sh) => sh,
586+
Err(_) => return Err(ParseError::InvalidScriptHash),
587+
};
588+
576589
Ok(Fallback::ScriptHash(sh))
577590
}
578591
_ => Err(ParseError::Skip)
@@ -655,12 +668,21 @@ impl Display for ParseError {
655668
ParseError::InvalidSegWitProgramLength => {
656669
f.write_str("fallback SegWit program is too long or too short")
657670
},
671+
ParseError::InvalidSegWitVersion => {
672+
f.write_str("fallback SegWit version is not valid")
673+
},
658674
ParseError::InvalidPubKeyHashLength => {
659675
f.write_str("fallback public key hash has a length unequal 20 bytes")
660676
},
677+
ParseError::InvalidPubKeyHash => {
678+
f.write_str("fallback public key hash is not a valid")
679+
},
661680
ParseError::InvalidScriptHashLength => {
662681
f.write_str("fallback script hash has a length unequal 32 bytes")
663682
},
683+
ParseError::InvalidScriptHash => {
684+
f.write_str("fallback script hash is not a valid")
685+
},
664686
ParseError::InvalidRecoveryId => {
665687
f.write_str("recovery id is out of range (should be in [0,3])")
666688
},
@@ -854,26 +876,29 @@ mod test {
854876
fn test_parse_fallback() {
855877
use crate::Fallback;
856878
use bech32::FromBase32;
879+
use bitcoin::{PubkeyHash, ScriptHash};
880+
use bitcoin::util::address::WitnessVersion;
881+
use bitcoin_hashes::Hash;
857882

858883
let cases = vec![
859884
(
860885
from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
861-
Ok(Fallback::PubKeyHash([
886+
Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
862887
0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
863888
0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
864-
]))
889+
]).unwrap()))
865890
),
866891
(
867892
from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
868-
Ok(Fallback::ScriptHash([
893+
Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
869894
0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
870895
0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
871-
]))
896+
]).unwrap()))
872897
),
873898
(
874899
from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
875900
Ok(Fallback::SegWitProgram {
876-
version: u5::try_from_u8(0).unwrap(),
901+
version: WitnessVersion::V0,
877902
program: Vec::from(&[
878903
0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
879904
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
@@ -907,36 +932,6 @@ mod test {
907932
}
908933
}
909934

910-
#[test]
911-
fn test_fallback_to_address() {
912-
use crate::Fallback;
913-
use crate::Currency;
914-
use crate::utils::address_from_fallback;
915-
use bech32::FromBase32;
916-
use bitcoin::Address;
917-
use core::str::FromStr;
918-
919-
let cases = vec![
920-
(
921-
from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
922-
Address::from_str("15WTXVVv4JHN992HjbRxMwmekcppRpzJrK").unwrap()
923-
),
924-
(
925-
from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
926-
Address::from_str("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX").unwrap()
927-
),
928-
(
929-
from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
930-
Address::from_str("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4").unwrap()
931-
),
932-
];
933-
934-
for (input, expected) in cases.into_iter() {
935-
let address = address_from_fallback(&Fallback::from_base32(&input).unwrap(), Currency::Bitcoin);
936-
assert_eq!(address, expected);
937-
}
938-
}
939-
940935
#[test]
941936
fn test_parse_route() {
942937
use lightning::routing::gossip::RoutingFees;

lightning-invoice/src/lib.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ use std::time::SystemTime;
4646

4747
use bech32::u5;
4848
use bitcoin::Address;
49+
use bitcoin::Network;
50+
use bitcoin::PubkeyHash;
51+
use bitcoin::Script;
52+
use bitcoin::ScriptHash;
53+
use bitcoin::util::address::WitnessVersion;
4954
use bitcoin_hashes::Hash;
5055
use bitcoin_hashes::sha256;
5156
use lightning::ln::PaymentSecret;
@@ -117,8 +122,11 @@ pub enum ParseError {
117122
PaddingError,
118123
IntegerOverflowError,
119124
InvalidSegWitProgramLength,
125+
InvalidSegWitVersion,
120126
InvalidPubKeyHashLength,
127+
InvalidPubKeyHash,
121128
InvalidScriptHashLength,
129+
InvalidScriptHash,
122130
InvalidRecoveryId,
123131
InvalidSliceLength(String),
124132

@@ -443,17 +451,16 @@ pub struct ExpiryTime(Duration);
443451
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
444452
pub struct MinFinalCltvExpiryDelta(pub u64);
445453

446-
// TODO: better types instead onf byte arrays
447454
/// Fallback address in case no LN payment is possible
448455
#[allow(missing_docs)]
449456
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
450457
pub enum Fallback {
451458
SegWitProgram {
452-
version: u5,
459+
version: WitnessVersion,
453460
program: Vec<u8>,
454461
},
455-
PubKeyHash([u8; 20]),
456-
ScriptHash([u8; 20]),
462+
PubKeyHash(PubkeyHash),
463+
ScriptHash(ScriptHash),
457464
}
458465

459466
/// Recoverable signature
@@ -1256,13 +1263,35 @@ impl Invoice {
12561263
///
12571264
/// (C-not exported) as we don't support Vec<&NonOpaqueType>
12581265
pub fn fallbacks(&self) -> Vec<&Fallback> {
1266+
1267+
12591268
self.signed_invoice.fallbacks()
12601269
}
12611270

1262-
/// Returns a list of all fallback addresses as `bitcoin::Address`es
1271+
/// Returns a list of all fallback addresses as [`Address`]es
12631272
pub fn fallback_addresses(&self) -> Vec<Address> {
12641273
self.fallbacks().iter().map(|fallback| {
1265-
utils::address_from_fallback(fallback, self.currency())
1274+
let network = match self.currency() {
1275+
Currency::Bitcoin => Network::Bitcoin,
1276+
Currency::BitcoinTestnet => Network::Testnet,
1277+
Currency::Regtest => Network::Regtest,
1278+
Currency::Simnet => Network::Regtest,
1279+
Currency::Signet => Network::Signet,
1280+
};
1281+
1282+
let script = match fallback {
1283+
Fallback::SegWitProgram { version, program } => {
1284+
Script::new_witness_program(*version, program.as_slice())
1285+
}
1286+
Fallback::PubKeyHash(pkh) => {
1287+
Script::new_p2pkh(&pkh)
1288+
}
1289+
Fallback::ScriptHash(sh) => {
1290+
Script::new_p2sh(&sh)
1291+
}
1292+
};
1293+
1294+
Address::from_script(&script, network).unwrap()
12661295
}).collect()
12671296
}
12681297

@@ -1938,7 +1967,7 @@ mod test {
19381967
.payee_pub_key(public_key.clone())
19391968
.expiry_time(Duration::from_secs(54321))
19401969
.min_final_cltv_expiry_delta(144)
1941-
.fallback(Fallback::PubKeyHash([0;20]))
1970+
.fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap()))
19421971
.private_route(route_1.clone())
19431972
.private_route(route_2.clone())
19441973
.description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
@@ -1964,7 +1993,10 @@ mod test {
19641993
assert_eq!(invoice.payee_pub_key(), Some(&public_key));
19651994
assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
19661995
assert_eq!(invoice.min_final_cltv_expiry_delta(), 144);
1967-
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
1996+
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap())]);
1997+
let address = Address::from_script(&Script::new_p2pkh(&PubkeyHash::from_slice(&[0;20]).unwrap()), Network::Testnet).unwrap();
1998+
assert_eq!(invoice.fallback_addresses(), vec![address]);
1999+
19682000
assert_eq!(invoice.private_routes(), vec![&PrivateRoute(route_1), &PrivateRoute(route_2)]);
19692001
assert_eq!(
19702002
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(u5::try_from_u8(v.to_num()).expect("0-16 < 32"))?;
333333
p.write_base32(writer)
334334
},
335335
Fallback::PubKeyHash(ref hash) => {

lightning-invoice/src/utils.rs

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
//! Convenient utilities to create an invoice.
22
3-
use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError, Fallback};
3+
use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
44

55
use crate::{prelude::*, Description, InvoiceDescription, Sha256};
66
use bech32::ToBase32;
7-
use bitcoin::util::address::WitnessVersion;
8-
use bitcoin::{Network, Address, Script, PubkeyHash, ScriptHash};
97
use bitcoin_hashes::Hash;
108
use lightning::chain;
119
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
@@ -18,7 +16,6 @@ use lightning::routing::gossip::RoutingFees;
1816
use lightning::routing::router::{RouteHint, RouteHintHop, Router};
1917
use lightning::util::logger::Logger;
2018
use secp256k1::PublicKey;
21-
use core::convert::TryFrom;
2219
use core::ops::Deref;
2320
use core::time::Duration;
2421

@@ -636,33 +633,6 @@ fn filter_channels<L: Deref>(
636633
.collect::<Vec<RouteHint>>()
637634
}
638635

639-
pub(crate) fn address_from_fallback(fallback: &Fallback, currency: Currency) -> Address {
640-
let network = match currency {
641-
Currency::Bitcoin => Network::Bitcoin,
642-
Currency::BitcoinTestnet => Network::Testnet,
643-
Currency::Regtest => Network::Regtest,
644-
Currency::Simnet => Network::Regtest,
645-
Currency::Signet => Network::Signet,
646-
};
647-
648-
let script = match fallback {
649-
Fallback::SegWitProgram { version, program } => {
650-
let version = WitnessVersion::try_from(*version).unwrap();
651-
Script::new_witness_program(version, program.as_slice())
652-
}
653-
Fallback::PubKeyHash(pkh) => {
654-
let pkh = PubkeyHash::from_slice(pkh).unwrap();
655-
Script::new_p2pkh(&pkh)
656-
}
657-
Fallback::ScriptHash(sh) => {
658-
let sh = ScriptHash::from_slice(sh).unwrap();
659-
Script::new_p2sh(&sh)
660-
}
661-
};
662-
663-
Address::from_script(&script, network).unwrap()
664-
}
665-
666636
#[cfg(test)]
667637
mod test {
668638
use core::time::Duration;

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)