X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-invoice%2Fsrc%2Fde.rs;h=bd9f4a5f6dedcf2b15502f821a6951a92ec5e99f;hb=21fee20a6d1e79a18a371e51e842da4b953ee070;hp=3f0548efb751799ecfbad44c0edf133dbe0dc571;hpb=62ca48f979010b28de23f0b060aa056d502a3436;p=rust-lightning diff --git a/lightning-invoice/src/de.rs b/lightning-invoice/src/de.rs index 3f0548efb..bd9f4a5f6 100644 --- a/lightning-invoice/src/de.rs +++ b/lightning-invoice/src/de.rs @@ -1,5 +1,6 @@ #[cfg(feature = "std")] use std::error; +#[cfg(not(feature = "std"))] use core::convert::TryFrom; use core::fmt; use core::fmt::{Display, Formatter}; @@ -9,23 +10,20 @@ use core::str::FromStr; use bech32::{u5, FromBase32}; -use bitcoin::{PubkeyHash, ScriptHash}; -use bitcoin::util::address::WitnessVersion; -use bitcoin_hashes::Hash; -use bitcoin_hashes::sha256; +use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion}; +use bitcoin::hashes::Hash; +use bitcoin::hashes::sha256; use crate::prelude::*; -use lightning::ln::PaymentSecret; +use lightning::ln::types::PaymentSecret; use lightning::routing::gossip::RoutingFees; use lightning::routing::router::{RouteHint, RouteHintHop}; -use num_traits::{CheckedAdd, CheckedMul}; - use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; use secp256k1::PublicKey; -use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp, +use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp, Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice, - constants, SignedRawBolt11Invoice, RawDataPart, InvoiceFeatures}; + constants, SignedRawBolt11Invoice, RawDataPart, Bolt11InvoiceFeatures}; use self::hrp_sm::parse_hrp; @@ -44,7 +42,11 @@ mod hrp_sm { } impl States { - fn next_state(&self, read_symbol: char) -> Result { + fn next_state(&self, read_byte: u8) -> Result { + let read_symbol = match char::from_u32(read_byte.into()) { + Some(symb) if symb.is_ascii() => symb, + _ => return Err(super::Bolt11ParseError::MalformedHRP), + }; match *self { States::Start => { if read_symbol == 'l' { @@ -120,7 +122,7 @@ mod hrp_sm { *range = Some(new_range); } - fn step(&mut self, c: char) -> Result<(), super::Bolt11ParseError> { + fn step(&mut self, c: u8) -> Result<(), super::Bolt11ParseError> { let next_state = self.state.next_state(c)?; match next_state { States::ParseCurrencyPrefix => { @@ -159,7 +161,7 @@ mod hrp_sm { pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> { let mut sm = StateMachine::new(); - for c in input.chars() { + for c in input.bytes() { sm.step(c)?; } @@ -292,7 +294,7 @@ impl FromStr for SignedRawBolt11Invoice { hrp.as_bytes(), &data[..data.len()-104] ), - signature: InvoiceSignature::from_base32(&data[data.len()-104..])?, + signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?, }) } } @@ -356,7 +358,7 @@ impl FromBase32 for PositiveTimestamp { if b32.len() != 7 { return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into())); } - let timestamp: u64 = parse_int_be(b32, 32) + let timestamp: u64 = parse_u64_be(b32) .expect("7*5bit < 64bit, no overflow possible"); match PositiveTimestamp::from_unix_timestamp(timestamp) { Ok(t) => Ok(t), @@ -365,33 +367,34 @@ impl FromBase32 for PositiveTimestamp { } } -impl FromBase32 for InvoiceSignature { +impl FromBase32 for Bolt11InvoiceSignature { type Err = Bolt11ParseError; fn from_base32(signature: &[u5]) -> Result { if signature.len() != 104 { - return Err(Bolt11ParseError::InvalidSliceLength("InvoiceSignature::from_base32()".into())); + return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into())); } let recoverable_signature_bytes = Vec::::from_base32(signature)?; let signature = &recoverable_signature_bytes[0..64]; let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?; - Ok(InvoiceSignature(RecoverableSignature::from_compact( + Ok(Bolt11InvoiceSignature(RecoverableSignature::from_compact( signature, recovery_id )?)) } } -pub(crate) fn parse_int_be(digits: &[U], base: T) -> Option - where T: CheckedAdd + CheckedMul + From + Default, - U: Into + Copy -{ - digits.iter().fold(Some(Default::default()), |acc, b| - acc - .and_then(|x| x.checked_mul(&base)) - .and_then(|x| x.checked_add(&(Into::::into(*b)).into())) - ) -} +macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => { + fn $name(digits: &[u5]) -> Option<$ty> { + digits.iter().fold(Some(Default::default()), |acc, b| + acc + .and_then(|x| x.checked_mul(32)) + .and_then(|x| x.checked_add((Into::::into(*b)).into())) + ) + } +} } +define_parse_int_be!(parse_u16_be, u16); +define_parse_int_be!(parse_u64_be, u64); fn parse_tagged_parts(data: &[u5]) -> Result, Bolt11ParseError> { let mut parts = Vec::::new(); @@ -404,7 +407,7 @@ fn parse_tagged_parts(data: &[u5]) -> Result, Bolt11ParseErr // Ignore tag at data[0], it will be handled in the TaggedField parsers and // parse the length to find the end of the tagged field's data - let len = parse_int_be(&data[1..3], 32).expect("can't overflow"); + let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize; let last_element = 3 + len; if data.len() < last_element { @@ -463,7 +466,7 @@ impl FromBase32 for TaggedField { constants::TAG_PAYMENT_METADATA => Ok(TaggedField::PaymentMetadata(Vec::::from_base32(field_data)?)), constants::TAG_FEATURES => - Ok(TaggedField::Features(InvoiceFeatures::from_base32(field_data)?)), + Ok(TaggedField::Features(Bolt11InvoiceFeatures::from_base32(field_data)?)), _ => { // "A reader MUST skip over unknown fields" Err(Bolt11ParseError::Skip) @@ -517,7 +520,7 @@ impl FromBase32 for ExpiryTime { type Err = Bolt11ParseError; fn from_base32(field_data: &[u5]) -> Result { - match parse_int_be::(field_data, 32) + match parse_u64_be(field_data) .map(ExpiryTime::from_seconds) { Some(t) => Ok(t), @@ -530,7 +533,7 @@ impl FromBase32 for MinFinalCltvExpiryDelta { type Err = Bolt11ParseError; fn from_base32(field_data: &[u5]) -> Result { - let expiry = parse_int_be::(field_data, 32); + let expiry = parse_u64_be(field_data); if let Some(expiry) = expiry { Ok(MinFinalCltvExpiryDelta(expiry)) } else { @@ -547,10 +550,10 @@ impl FromBase32 for Fallback { return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields); } - let version = field_data[0]; + let version = field_data[0].to_u8(); let bytes = Vec::::from_base32(&field_data[1..])?; - match version.to_u8() { + match version { 0..=16 => { if bytes.len() < 2 || bytes.len() > 40 { return Err(Bolt11ParseError::InvalidSegWitProgramLength); @@ -564,14 +567,14 @@ impl FromBase32 for Fallback { 17 => { let pkh = match PubkeyHash::from_slice(&bytes) { Ok(pkh) => pkh, - Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidPubKeyHashLength), + Err(_) => return Err(Bolt11ParseError::InvalidPubKeyHashLength), }; Ok(Fallback::PubKeyHash(pkh)) } 18 => { let sh = match ScriptHash::from_slice(&bytes) { Ok(sh) => sh, - Err(bitcoin_hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidScriptHashLength), + Err(_) => return Err(Bolt11ParseError::InvalidScriptHashLength), }; Ok(Fallback::ScriptHash(sh)) } @@ -602,12 +605,12 @@ impl FromBase32 for PrivateRoute { let hop = RouteHintHop { src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?, - short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"), + short_channel_id: u64::from_be_bytes(channel_id), fees: RoutingFees { - base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"), - proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"), + base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")), + proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")), }, - cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"), + cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")), htlc_minimum_msat: None, htlc_maximum_msat: None, }; @@ -726,8 +729,8 @@ mod test { use crate::de::Bolt11ParseError; use secp256k1::PublicKey; use bech32::u5; - use bitcoin_hashes::hex::FromHex; - use bitcoin_hashes::sha256; + use bitcoin::hashes::sha256; + use std::str::FromStr; const CHARSET_REV: [i8; 128] = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -761,12 +764,16 @@ mod test { #[test] fn test_parse_int_from_bytes_be() { - use crate::de::parse_int_be; - - assert_eq!(parse_int_be::(&[1, 2, 3, 4], 256), Some(16909060)); - assert_eq!(parse_int_be::(&[1, 3], 32), Some(35)); - assert_eq!(parse_int_be::(&[255, 255, 255, 255], 256), Some(4294967295)); - assert_eq!(parse_int_be::(&[1, 0, 0, 0, 0], 256), None); + use crate::de::parse_u16_be; + + assert_eq!(parse_u16_be(&[ + u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(), + u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()] + ), Some(34916)); + assert_eq!(parse_u16_be(&[ + u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(), + u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()] + ), None); } #[test] @@ -778,7 +785,7 @@ mod test { "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes() ); - let hash = sha256::Hash::from_hex( + let hash = sha256::Hash::from_str( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap(); let expected = Ok(Sha256(hash)); @@ -854,9 +861,8 @@ 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; + use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion}; + use bitcoin::hashes::Hash; let cases = vec![ ( @@ -916,7 +922,6 @@ mod test { use lightning::routing::router::{RouteHint, RouteHintHop}; use crate::PrivateRoute; use bech32::FromBase32; - use crate::de::parse_int_be; let input = from_bech32( "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\ @@ -932,7 +937,7 @@ mod test { 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55 ][..] ).unwrap(), - short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"), + short_channel_id: 0x0102030405060708, fees: RoutingFees { base_msat: 1, proportional_millionths: 20, @@ -949,7 +954,7 @@ mod test { 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55 ][..] ).unwrap(), - short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"), + short_channel_id: 0x030405060708090a, fees: RoutingFees { base_msat: 2, proportional_millionths: 30, @@ -969,14 +974,14 @@ mod test { #[test] fn test_payment_secret_and_features_de_and_ser() { - use lightning::ln::features::InvoiceFeatures; + use lightning::ln::features::Bolt11InvoiceFeatures; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; use crate::TaggedField::*; - use crate::{SiPrefix, SignedRawBolt11Invoice, InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, + use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp}; // Feature bits 9, 15, and 99 are set. - let expected_features = InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]); + let expected_features = Bolt11InvoiceFeatures::from_le_bytes(vec![0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]); let invoice_str = "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu"; let invoice = SignedRawBolt11Invoice { raw_invoice: RawBolt11Invoice { @@ -988,7 +993,7 @@ mod test { data: RawDataPart { timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(), tagged_fields: vec ! [ - PaymentHash(Sha256(sha256::Hash::from_hex( + PaymentHash(Sha256(sha256::Hash::from_str( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap())).into(), Description(crate::Description::new("coffee beans".to_owned()).unwrap()).into(), @@ -998,7 +1003,7 @@ mod test { hash: [0xb1, 0x96, 0x46, 0xc3, 0xbc, 0x56, 0x76, 0x1d, 0x20, 0x65, 0x6e, 0x0e, 0x32, 0xec, 0xd2, 0x69, 0x27, 0xb7, 0x62, 0x6e, 0x2a, 0x8b, 0xe6, 0x97, 0x71, 0x9f, 0xf8, 0x7e, 0x44, 0x54, 0x55, 0xb9], - signature: InvoiceSignature(RecoverableSignature::from_compact( + signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact( &[0xd7, 0x90, 0x4c, 0xc4, 0xb7, 0x4a, 0x22, 0x26, 0x9c, 0x68, 0xc1, 0xdf, 0x68, 0xa9, 0x6c, 0x21, 0x4d, 0x65, 0x1b, 0x93, 0x76, 0xe9, 0xf1, 0x64, 0xd3, 0x60, 0x4d, 0xa4, 0xb7, 0xde, 0xcc, 0xce, 0x0e, 0x82, 0xaa, 0xab, 0x4c, 0x85, 0xd3, @@ -1018,7 +1023,7 @@ mod test { fn test_raw_signed_invoice_deserialization() { use crate::TaggedField::*; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; - use crate::{SignedRawBolt11Invoice, InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, + use crate::{SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp}; assert_eq!( @@ -1035,7 +1040,7 @@ mod test { data: RawDataPart { timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(), tagged_fields: vec ! [ - PaymentHash(Sha256(sha256::Hash::from_hex( + PaymentHash(Sha256(sha256::Hash::from_str( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap())).into(), Description( @@ -1051,7 +1056,7 @@ mod test { 0x7b, 0x1d, 0x85, 0x8d, 0xb1, 0xd1, 0xf7, 0xab, 0x71, 0x37, 0xdc, 0xb7, 0x83, 0x5d, 0xb2, 0xec, 0xd5, 0x18, 0xe1, 0xc9 ], - signature: InvoiceSignature(RecoverableSignature::from_compact( + signature: Bolt11InvoiceSignature(RecoverableSignature::from_compact( & [ 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a, 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,