Skip to content

add Fallback getter that returns Address #2023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lightning-invoice/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ num-traits = { version = "0.2.8", default-features = false }
bitcoin_hashes = { version = "0.11", default-features = false }
hashbrown = { version = "0.8", optional = true }
serde = { version = "1.0.118", optional = true }
bitcoin = { version = "0.29.0", default-features = false }

[dev-dependencies]
lightning = { version = "0.0.113", path = "../lightning", default-features = false, features = ["_test_utils"] }
Expand Down
37 changes: 20 additions & 17 deletions lightning-invoice/src/de.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(feature = "std")]
use std::error;
use core::convert::TryFrom;
use core::fmt;
use core::fmt::{Display, Formatter};
use core::num::ParseIntError;
Expand All @@ -9,6 +10,8 @@ use core::str::FromStr;
use bech32;
use bech32::{u5, FromBase32};

use bitcoin::{PubkeyHash, ScriptHash};
use bitcoin::util::address::WitnessVersion;
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256;
use crate::prelude::*;
Expand Down Expand Up @@ -552,27 +555,24 @@ impl FromBase32 for Fallback {
if bytes.len() < 2 || bytes.len() > 40 {
return Err(ParseError::InvalidSegWitProgramLength);
}

let version = WitnessVersion::try_from(version).expect("0 through 16 are valid SegWit versions");
Ok(Fallback::SegWitProgram {
version: version,
program: bytes
})
},
17 => {
if bytes.len() != 20 {
return Err(ParseError::InvalidPubKeyHashLength);
}
//TODO: refactor once const generics are available
let mut pkh = [0u8; 20];
pkh.copy_from_slice(&bytes);
let pkh = match PubkeyHash::from_slice(&bytes) {
Ok(pkh) => pkh,
Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidPubKeyHashLength),
};
Ok(Fallback::PubKeyHash(pkh))
}
18 => {
if bytes.len() != 20 {
return Err(ParseError::InvalidScriptHashLength);
}
let mut sh = [0u8; 20];
sh.copy_from_slice(&bytes);
let sh = match ScriptHash::from_slice(&bytes) {
Ok(sh) => sh,
Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(ParseError::InvalidScriptHashLength),
};
Ok(Fallback::ScriptHash(sh))
}
_ => Err(ParseError::Skip)
Expand Down Expand Up @@ -854,26 +854,29 @@ mod test {
fn test_parse_fallback() {
use crate::Fallback;
use bech32::FromBase32;
use bitcoin::{PubkeyHash, ScriptHash};
use bitcoin::util::address::WitnessVersion;
use bitcoin_hashes::Hash;

let cases = vec![
(
from_bech32("3x9et2e20v6pu37c5d9vax37wxq72un98".as_bytes()),
Ok(Fallback::PubKeyHash([
Ok(Fallback::PubKeyHash(PubkeyHash::from_slice(&[
0x31, 0x72, 0xb5, 0x65, 0x4f, 0x66, 0x83, 0xc8, 0xfb, 0x14, 0x69, 0x59, 0xd3,
0x47, 0xce, 0x30, 0x3c, 0xae, 0x4c, 0xa7
]))
]).unwrap()))
),
(
from_bech32("j3a24vwu6r8ejrss3axul8rxldph2q7z9".as_bytes()),
Ok(Fallback::ScriptHash([
Ok(Fallback::ScriptHash(ScriptHash::from_slice(&[
0x8f, 0x55, 0x56, 0x3b, 0x9a, 0x19, 0xf3, 0x21, 0xc2, 0x11, 0xe9, 0xb9, 0xf3,
0x8c, 0xdf, 0x68, 0x6e, 0xa0, 0x78, 0x45
]))
]).unwrap()))
),
(
from_bech32("qw508d6qejxtdg4y5r3zarvary0c5xw7k".as_bytes()),
Ok(Fallback::SegWitProgram {
version: u5::try_from_u8(0).unwrap(),
version: WitnessVersion::V0,
program: Vec::from(&[
0x75u8, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
Expand Down
46 changes: 38 additions & 8 deletions lightning-invoice/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ extern crate serde;
use std::time::SystemTime;

use bech32::u5;
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256;
use bitcoin::{Address, Network, PubkeyHash, ScriptHash};
use bitcoin::util::address::{Payload, WitnessVersion};
use bitcoin_hashes::{Hash, sha256};
use lightning::ln::PaymentSecret;
use lightning::ln::features::InvoiceFeatures;
#[cfg(any(doc, test))]
Expand Down Expand Up @@ -442,17 +443,16 @@ pub struct ExpiryTime(Duration);
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct MinFinalCltvExpiryDelta(pub u64);

// TODO: better types instead onf byte arrays
/// Fallback address in case no LN payment is possible
#[allow(missing_docs)]
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum Fallback {
SegWitProgram {
version: u5,
version: WitnessVersion,
program: Vec<u8>,
},
PubKeyHash([u8; 20]),
ScriptHash([u8; 20]),
PubKeyHash(PubkeyHash),
ScriptHash(ScriptHash),
}

/// Recoverable signature
Expand Down Expand Up @@ -1258,6 +1258,33 @@ impl Invoice {
self.signed_invoice.fallbacks()
}

/// Returns a list of all fallback addresses as [`Address`]es
pub fn fallback_addresses(&self) -> Vec<Address> {
self.fallbacks().iter().map(|fallback| {
let network = match self.currency() {
Currency::Bitcoin => Network::Bitcoin,
Currency::BitcoinTestnet => Network::Testnet,
Currency::Regtest => Network::Regtest,
Currency::Simnet => Network::Regtest,
Currency::Signet => Network::Signet,
};

let payload = match fallback {
Fallback::SegWitProgram { version, program } => {
Payload::WitnessProgram { version: *version, program: program.to_vec() }
}
Fallback::PubKeyHash(pkh) => {
Payload::PubkeyHash(*pkh)
}
Fallback::ScriptHash(sh) => {
Payload::ScriptHash(*sh)
}
};

Address { payload, network }
}).collect()
}

/// Returns a list of all routes included in the invoice
pub fn private_routes(&self) -> Vec<&PrivateRoute> {
self.signed_invoice.private_routes()
Expand Down Expand Up @@ -1567,6 +1594,7 @@ impl<'de> Deserialize<'de> for Invoice {

#[cfg(test)]
mod test {
use bitcoin::Script;
use bitcoin_hashes::hex::FromHex;
use bitcoin_hashes::sha256;

Expand Down Expand Up @@ -1930,7 +1958,7 @@ mod test {
.payee_pub_key(public_key.clone())
.expiry_time(Duration::from_secs(54321))
.min_final_cltv_expiry_delta(144)
.fallback(Fallback::PubKeyHash([0;20]))
.fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap()))
.private_route(route_1.clone())
.private_route(route_2.clone())
.description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap())
Expand All @@ -1956,7 +1984,9 @@ mod test {
assert_eq!(invoice.payee_pub_key(), Some(&public_key));
assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
assert_eq!(invoice.min_final_cltv_expiry_delta(), 144);
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap())]);
let address = Address::from_script(&Script::new_p2pkh(&PubkeyHash::from_slice(&[0;20]).unwrap()), Network::Testnet).unwrap();
assert_eq!(invoice.fallback_addresses(), vec![address]);
assert_eq!(invoice.private_routes(), vec![&PrivateRoute(route_1), &PrivateRoute(route_2)]);
assert_eq!(
invoice.description(),
Expand Down
2 changes: 1 addition & 1 deletion lightning-invoice/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ impl ToBase32 for Fallback {
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
match *self {
Fallback::SegWitProgram {version: v, program: ref p} => {
writer.write_u5(v)?;
writer.write_u5(Into::<u5>::into(v))?;
p.write_base32(writer)
},
Fallback::PubKeyHash(ref hash) => {
Expand Down
13 changes: 7 additions & 6 deletions lightning-invoice/tests/ser_de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ extern crate lightning_invoice;
extern crate secp256k1;
extern crate hex;

use bitcoin::util::address::WitnessVersion;
use bitcoin::{PubkeyHash, ScriptHash};
use bitcoin_hashes::hex::FromHex;
use bitcoin_hashes::{sha256, Hash};
use bech32::u5;
use lightning::ln::PaymentSecret;
use lightning::routing::gossip::RoutingFees;
use lightning::routing::router::{RouteHint, RouteHintHop};
Expand Down Expand Up @@ -115,7 +116,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.fallback(Fallback::PubKeyHash([49, 114, 181, 101, 79, 102, 131, 200, 251, 20, 105, 89, 211, 71, 206, 48, 60, 174, 76, 167]))
.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()))
.build_raw()
.unwrap()
.sign(|_| {
Expand All @@ -137,7 +138,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.fallback(Fallback::PubKeyHash([4, 182, 31, 125, 193, 234, 13, 201, 148, 36, 70, 76, 196, 6, 77, 197, 100, 217, 30, 137]))
.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()))
.private_route(RouteHint(vec![RouteHintHop {
src_node_id: PublicKey::from_slice(&hex::decode(
"029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"
Expand Down Expand Up @@ -176,7 +177,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.fallback(Fallback::ScriptHash([143, 85, 86, 59, 154, 25, 243, 33, 194, 17, 233, 185, 243, 140, 223, 104, 110, 160, 120, 69]))
.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()))
.build_raw()
.unwrap()
.sign(|_| {
Expand All @@ -198,7 +199,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
.fallback(Fallback::SegWitProgram { version: WitnessVersion::V0,
program: vec![117, 30, 118, 232, 25, 145, 150, 212, 84, 148, 28, 69, 209, 179, 163, 35, 241, 67, 59, 214]
})
.build_raw()
Expand All @@ -222,7 +223,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
.fallback(Fallback::SegWitProgram { version: WitnessVersion::V0,
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]
})
.build_raw()
Expand Down