//! raw socket events into your non-internet-facing system and then send routing events back to
//! track the network on the less-secure system.
-use secp256k1::key::PublicKey;
-use secp256k1::Signature;
-use secp256k1;
-use bitcoin_hashes::sha256d::Hash as Sha256dHash;
+use bitcoin::secp256k1::key::PublicKey;
+use bitcoin::secp256k1::Signature;
+use bitcoin::secp256k1;
use bitcoin::blockdata::script::Script;
+use bitcoin::hash_types::{Txid, BlockHash};
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
-use std::error::Error;
use std::{cmp, fmt};
use std::io::Read;
use std::result::Result;
use util::events;
use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt};
-use ln::channelmanager::{PaymentPreimage, PaymentHash};
+use ln::channelmanager::{PaymentPreimage, PaymentHash, PaymentSecret};
+
+/// 21 million * 10^8 * 1000
+pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
/// An error in decoding a message or struct.
#[derive(Debug)]
/// An open_channel message to be sent or received from a peer
#[derive(Clone)]
pub struct OpenChannel {
- pub(crate) chain_hash: Sha256dHash,
+ pub(crate) chain_hash: BlockHash,
pub(crate) temporary_channel_id: [u8; 32],
pub(crate) funding_satoshis: u64,
pub(crate) push_msat: u64,
#[derive(Clone)]
pub struct FundingCreated {
pub(crate) temporary_channel_id: [u8; 32],
- pub(crate) funding_txid: Sha256dHash,
+ pub(crate) funding_txid: Txid,
pub(crate) funding_output_index: u16,
pub(crate) signature: Signature,
}
#[derive(PartialEq, Clone, Debug)]
pub struct UnsignedChannelAnnouncement {
pub(crate) features: ChannelFeatures,
- pub(crate) chain_hash: Sha256dHash,
+ pub(crate) chain_hash: BlockHash,
pub(crate) short_channel_id: u64,
/// One of the two node_ids which are endpoints of this channel
pub node_id_1: PublicKey,
#[derive(PartialEq, Clone, Debug)]
pub(crate) struct UnsignedChannelUpdate {
- pub(crate) chain_hash: Sha256dHash,
+ pub(crate) chain_hash: BlockHash,
pub(crate) short_channel_id: u64,
pub(crate) timestamp: u32,
pub(crate) flags: u16,
/// Gets a subset of the channel announcements and updates required to dump our routing table
/// to a remote node, starting at the short_channel_id indicated by starting_point and
/// including the batch_amount entries immediately higher in numerical value than starting_point.
- fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, ChannelUpdate, ChannelUpdate)>;
+ fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)>;
/// Gets a subset of the node announcements required to dump our routing table to a remote node,
/// starting at the node *after* the provided publickey and including batch_amount entries
/// immediately higher (as defined by <PublicKey as Ord>::cmp) than starting_point.
}
mod fuzzy_internal_msgs {
+ use ln::channelmanager::PaymentSecret;
+
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
// them from untrusted input):
+ #[derive(Clone)]
+ pub(crate) struct FinalOnionHopData {
+ pub(crate) payment_secret: PaymentSecret,
+ /// The total value, in msat, of the payment as received by the ultimate recipient.
+ /// Message serialization may panic if this value is more than 21 million Bitcoin.
+ pub(crate) total_msat: u64,
+ }
pub(crate) enum OnionHopDataFormat {
Legacy { // aka Realm-0
NonFinalNode {
short_channel_id: u64,
},
- FinalNode,
+ FinalNode {
+ payment_data: Option<FinalOnionHopData>,
+ },
}
pub struct OnionHopData {
pub(crate) format: OnionHopDataFormat,
+ /// The value, in msat, of the payment after this hop's fee is deducted.
+ /// Message serialization may panic if this value is more than 21 million Bitcoin.
pub(crate) amt_to_forward: u64,
pub(crate) outgoing_cltv_value: u32,
// 12 bytes of 0-padding for Legacy format
pub(crate) data: Vec<u8>,
}
-impl Error for DecodeError {
- fn description(&self) -> &str {
- match *self {
- DecodeError::UnknownVersion => "Unknown realm byte in Onion packet",
- DecodeError::UnknownRequiredFeature => "Unknown required feature preventing decode",
- DecodeError::InvalidValue => "Nonsense bytes didn't map to the type they were interpreted as",
- DecodeError::ShortRead => "Packet extended beyond the provided bytes",
- DecodeError::BadLengthDescriptor => "A length descriptor in the packet didn't describe the later data correctly",
- DecodeError::Io(ref e) => e.description(),
- }
- }
-}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str(self.description())
+ match *self {
+ DecodeError::UnknownVersion => f.write_str("Unknown realm byte in Onion packet"),
+ DecodeError::UnknownRequiredFeature => f.write_str("Unknown required feature preventing decode"),
+ DecodeError::InvalidValue => f.write_str("Nonsense bytes didn't map to the type they were interpreted as"),
+ DecodeError::ShortRead => f.write_str("Packet extended beyond the provided bytes"),
+ DecodeError::BadLengthDescriptor => f.write_str("A length descriptor in the packet didn't describe the later data correctly"),
+ DecodeError::Io(ref e) => e.fmt(f),
+ }
}
}
onion_routing_packet
});
+impl Writeable for FinalOnionHopData {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
+ w.size_hint(32 + 8 - (self.total_msat.leading_zeros()/8) as usize);
+ self.payment_secret.0.write(w)?;
+ HighZeroBytesDroppedVarInt(self.total_msat).write(w)
+ }
+}
+
+impl Readable for FinalOnionHopData {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let secret: [u8; 32] = Readable::read(r)?;
+ let amt: HighZeroBytesDroppedVarInt<u64> = Readable::read(r)?;
+ Ok(Self { payment_secret: PaymentSecret(secret), total_msat: amt.0 })
+ }
+}
+
impl Writeable for OnionHopData {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
w.size_hint(33);
+ // Note that this should never be reachable if Rust-Lightning generated the message, as we
+ // check values are sane long before we get here, though its possible in the future
+ // user-generated messages may hit this.
+ if self.amt_to_forward > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
match self.format {
OnionHopDataFormat::Legacy { short_channel_id } => {
0u8.write(w)?;
(6, short_channel_id)
});
},
- OnionHopDataFormat::FinalNode => {
+ OnionHopDataFormat::FinalNode { payment_data: Some(ref final_data) } => {
+ if final_data.total_msat > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
+ encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
+ (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)),
+ (8, final_data)
+ });
+ },
+ OnionHopDataFormat::FinalNode { payment_data: None } => {
encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
(4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value))
let mut amt = HighZeroBytesDroppedVarInt(0u64);
let mut cltv_value = HighZeroBytesDroppedVarInt(0u32);
let mut short_id: Option<u64> = None;
+ let mut payment_data: Option<FinalOnionHopData> = None;
decode_tlv!(&mut rd, {
(2, amt),
(4, cltv_value)
}, {
- (6, short_id)
+ (6, short_id),
+ (8, payment_data)
});
rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
let format = if let Some(short_channel_id) = short_id {
+ if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
OnionHopDataFormat::NonFinalNode {
short_channel_id,
}
} else {
- OnionHopDataFormat::FinalNode
+ if let &Some(ref data) = &payment_data {
+ if data.total_msat > MAX_VALUE_MSAT {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+ OnionHopDataFormat::FinalNode {
+ payment_data
+ }
};
(format, amt.0, cltv_value.0)
} else {
(format, amt, cltv_value)
};
+ if amt > MAX_VALUE_MSAT {
+ return Err(DecodeError::InvalidValue);
+ }
Ok(OnionHopData {
format,
amt_to_forward: amt,
mod tests {
use hex;
use ln::msgs;
- use ln::msgs::{ChannelFeatures, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
- use ln::channelmanager::{PaymentPreimage, PaymentHash};
+ use ln::msgs::{ChannelFeatures, FinalOnionHopData, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
+ use ln::channelmanager::{PaymentPreimage, PaymentHash, PaymentSecret};
use util::ser::{Writeable, Readable};
- use bitcoin_hashes::sha256d::Hash as Sha256dHash;
- use bitcoin_hashes::hex::FromHex;
+ use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
use bitcoin::network::constants::Network;
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
+ use bitcoin::hash_types::{Txid, BlockHash};
- use secp256k1::key::{PublicKey,SecretKey};
- use secp256k1::{Secp256k1, Message};
+ use bitcoin::secp256k1::key::{PublicKey,SecretKey};
+ use bitcoin::secp256k1::{Secp256k1, Message};
use std::io::Cursor;
let sig_2 = get_sig_on!(privkey_2, secp_ctx, String::from("01010101010101010101010101010101"));
let sig_3 = get_sig_on!(privkey_3, secp_ctx, String::from("01010101010101010101010101010101"));
let sig_4 = get_sig_on!(privkey_4, secp_ctx, String::from("01010101010101010101010101010101"));
- let mut features = ChannelFeatures::supported();
+ let mut features = ChannelFeatures::known();
if unknown_features_bits {
features = ChannelFeatures::from_le_bytes(vec![0xFF, 0xFF]);
}
let unsigned_channel_announcement = msgs::UnsignedChannelAnnouncement {
features,
- chain_hash: if !non_bitcoin_chain_hash { Sha256dHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { Sha256dHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
+ chain_hash: if !non_bitcoin_chain_hash { BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { BlockHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
short_channel_id: 2316138423780173,
node_id_1: pubkey_1,
node_id_2: pubkey_2,
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let unsigned_channel_update = msgs::UnsignedChannelUpdate {
- chain_hash: if !non_bitcoin_chain_hash { Sha256dHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { Sha256dHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
+ chain_hash: if !non_bitcoin_chain_hash { BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { BlockHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
short_channel_id: 2316138423780173,
timestamp: 20190119,
flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 } | if htlc_maximum_msat { 1 << 8 } else { 0 },
let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let open_channel = msgs::OpenChannel {
- chain_hash: if !non_bitcoin_chain_hash { Sha256dHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { Sha256dHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
+ chain_hash: if !non_bitcoin_chain_hash { BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap() } else { BlockHash::from_hex("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943").unwrap() },
temporary_channel_id: [2; 32],
funding_satoshis: 1311768467284833366,
push_msat: 2536655962884945560,
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let funding_created = msgs::FundingCreated {
temporary_channel_id: [2; 32],
- funding_txid: Sha256dHash::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
+ funding_txid: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
funding_output_index: 255,
signature: sig_1,
};
#[test]
fn encoding_final_onion_hop_data() {
let mut msg = msgs::OnionHopData {
- format: OnionHopDataFormat::FinalNode,
+ format: OnionHopDataFormat::FinalNode {
+ payment_data: None,
+ },
amt_to_forward: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
};
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
assert_eq!(encoded_value, target_value);
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
- if let OnionHopDataFormat::FinalNode = msg.format { } else { panic!(); }
+ if let OnionHopDataFormat::FinalNode { payment_data: None } = msg.format { } else { panic!(); }
+ assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
+ assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
+ }
+
+ #[test]
+ fn encoding_final_onion_hop_data_with_secret() {
+ let expected_payment_secret = PaymentSecret([0x42u8; 32]);
+ let mut msg = msgs::OnionHopData {
+ format: OnionHopDataFormat::FinalNode {
+ payment_data: Some(FinalOnionHopData {
+ payment_secret: expected_payment_secret,
+ total_msat: 0x1badca1f
+ }),
+ },
+ amt_to_forward: 0x0badf00d01020304,
+ outgoing_cltv_value: 0xffffffff,
+ };
+ let encoded_value = msg.encode();
+ let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
+ assert_eq!(encoded_value, target_value);
+ msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
+ if let OnionHopDataFormat::FinalNode {
+ payment_data: Some(FinalOnionHopData {
+ payment_secret,
+ total_msat: 0x1badca1f
+ })
+ } = msg.format {
+ assert_eq!(payment_secret, expected_payment_secret);
+ } else { panic!(); }
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
}