From 4d77e9d75216d84795bfbaf6a9b41f94637fac1c Mon Sep 17 00:00:00 2001 From: Yuko Roodt Date: Fri, 30 Nov 2018 11:51:37 +0200 Subject: [PATCH] Added tests to check the bolt 2 specs for Sending Node Channel --- fuzz/fuzz_targets/full_stack_target.rs | 23 +++++++++ src/chain/keysinterface.rs | 23 +++++++++ src/ln/channel.rs | 8 +++- src/ln/functional_tests.rs | 64 +++++++++++++++++++++++++- src/util/rng.rs | 17 ------- src/util/test_utils.rs | 9 ++++ 6 files changed, 124 insertions(+), 20 deletions(-) diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs index 067c73d21..9930a44d2 100644 --- a/fuzz/fuzz_targets/full_stack_target.rs +++ b/fuzz/fuzz_targets/full_stack_target.rs @@ -281,6 +281,29 @@ impl KeysInterface for KeyProvider { fill_bytes(&mut session_key); SecretKey::from_slice(&session_key).unwrap() } + + fn get_channel_id(&self) -> [u8; 32] { + let mut channel_id = [0; 32]; + fill_bytes(&mut channel_id); + for i in 0..4 { + // byteswap the u64s in channel_id to make it distinct from get_session_key (and match + // old code that wrote out different endianness). + let mut t; + t = channel_id[i*8 + 0]; + channel_id[i*8 + 0] = channel_id[i*8 + 7]; + channel_id[i*8 + 7] = t; + t = channel_id[i*8 + 1]; + channel_id[i*8 + 1] = channel_id[i*8 + 6]; + channel_id[i*8 + 6] = t; + t = channel_id[i*8 + 2]; + channel_id[i*8 + 2] = channel_id[i*8 + 5]; + channel_id[i*8 + 5] = t; + t = channel_id[i*8 + 3]; + channel_id[i*8 + 3] = channel_id[i*8 + 4]; + channel_id[i*8 + 4] = t; + } + channel_id + } } #[inline] diff --git a/src/chain/keysinterface.rs b/src/chain/keysinterface.rs index 53191faf2..9369d3c35 100644 --- a/src/chain/keysinterface.rs +++ b/src/chain/keysinterface.rs @@ -80,6 +80,10 @@ pub trait KeysInterface: Send + Sync { fn get_channel_keys(&self, inbound: bool) -> ChannelKeys; /// Get a secret for construting an onion packet fn get_session_key(&self) -> SecretKey; + /// Get a unique temporary channel id. Channels will be referred to by this until the funding + /// transaction is created, at which point they will use the outpoint in the funding + /// transaction. + fn get_channel_id(&self) -> [u8; 32]; } /// Set of lightning keys needed to operate a channel as described in BOLT 3 @@ -124,6 +128,8 @@ pub struct KeysManager { channel_child_index: AtomicUsize, session_master_key: ExtendedPrivKey, session_child_index: AtomicUsize, + channel_id_master_key: ExtendedPrivKey, + channel_id_child_index: AtomicUsize, logger: Arc, } @@ -151,6 +157,7 @@ impl KeysManager { }; let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3)).expect("Your RNG is busted"); let session_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4)).expect("Your RNG is busted"); + let channel_id_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5)).expect("Your RNG is busted"); KeysManager { secp_ctx, node_secret, @@ -160,6 +167,8 @@ impl KeysManager { channel_child_index: AtomicUsize::new(0), session_master_key, session_child_index: AtomicUsize::new(0), + channel_id_master_key, + channel_id_child_index: AtomicUsize::new(0), logger, } @@ -246,4 +255,18 @@ impl KeysInterface for KeysManager { sha.input(&child_privkey.secret_key[..]); SecretKey::from_slice(&Sha256::from_engine(sha).into_inner()).expect("Your RNG is busted") } + + fn get_channel_id(&self) -> [u8; 32] { + let mut sha = Sha256::engine(); + + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); + sha.input(&byte_utils::be32_to_array(now.subsec_nanos())); + sha.input(&byte_utils::be64_to_array(now.as_secs())); + + let child_ix = self.channel_id_child_index.fetch_add(1, Ordering::AcqRel); + let child_privkey = self.channel_id_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32)).expect("Your RNG is busted"); + sha.input(&child_privkey.secret_key[..]); + + (Sha256::from_engine(sha).into_inner()) + } } diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 10b0725cd..326df90dc 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -23,7 +23,7 @@ use ln::chan_utils; use chain::chaininterface::{FeeEstimator,ConfirmationTarget}; use chain::transaction::OutPoint; use chain::keysinterface::{ChannelKeys, KeysInterface}; -use util::{transaction_utils,rng}; +use util::transaction_utils; use util::ser::{Readable, ReadableArgs, Writeable, Writer, WriterWriteAdaptor}; use util::logger::Logger; use util::errors::APIError; @@ -350,7 +350,10 @@ pub const OUR_MAX_HTLCS: u16 = 50; //TODO const UNCONF_THRESHOLD: u32 = 6; /// The amount of time we require our counterparty wait to claim their money (ie time between when /// we, or our watchtower, must check for them having broadcast a theft transaction). +#[cfg(not(test))] const BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7; //TODO? +#[cfg(test)] +pub const BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7; //TODO? /// The amount of time we're willing to wait to claim money back to us const MAX_LOCAL_BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 14; /// Exposing these two constants for use in test in ChannelMonitor @@ -444,7 +447,7 @@ impl Channel { user_id: user_id, config: config.channel_options.clone(), - channel_id: rng::rand_u832(), + channel_id: keys_provider.get_channel_id(), channel_state: ChannelState::OurInitSent as u32, channel_outbound: true, secp_ctx: secp_ctx, @@ -3950,6 +3953,7 @@ mod tests { fn get_channel_keys(&self, _inbound: bool) -> ChannelKeys { self.chan_keys.clone() } fn get_session_key(&self) -> SecretKey { panic!(); } + fn get_channel_id(&self) -> [u8; 32] { [0; 32] } } #[test] diff --git a/src/ln/functional_tests.rs b/src/ln/functional_tests.rs index 6a2b6d3db..b6463bf32 100644 --- a/src/ln/functional_tests.rs +++ b/src/ln/functional_tests.rs @@ -7,7 +7,7 @@ use chain::transaction::OutPoint; use chain::chaininterface::{ChainListener, ChainWatchInterface}; use chain::keysinterface::{KeysInterface, SpendableOutputDescriptor}; use chain::keysinterface; -use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC}; +use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC, BREAKDOWN_TIMEOUT}; use ln::channelmanager::{ChannelManager,ChannelManagerReadArgs,HTLCForwardInfo,RAACommitmentOrder, PaymentPreimage, PaymentHash}; use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr, CLTV_CLAIM_BUFFER, HTLC_FAIL_TIMEOUT_BLOCKS, ManyChannelMonitor}; use ln::channel::{ACCEPTED_HTLC_SCRIPT_WEIGHT, OFFERED_HTLC_SCRIPT_WEIGHT}; @@ -6716,6 +6716,68 @@ fn test_onion_failure() { }, ||{}, true, Some(21), None); } +#[test] +#[should_panic] +fn bolt2_open_channel_sending_node_checks_part1() { //This test needs to be on its own as we are catching a panic + let nodes = create_network(2); + //Force duplicate channel ids + for node in nodes.iter() { + *node.keys_manager.override_channel_id_priv.lock().unwrap() = Some([0; 32]); + } + + // BOLT #2 spec: Sending node must ensure temporary_channel_id is unique from any other channel ID with the same peer. + let channel_value_satoshis=10000; + let push_msat=10001; + nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42).unwrap(); + let node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id()); + nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &node0_to_1_send_open_channel).unwrap(); + + //Create a second channel with a channel_id collision + assert!(nodes[0].node.create_channel(nodes[0].node.get_our_node_id(), channel_value_satoshis, push_msat, 42).is_err()); +} + +#[test] +fn bolt2_open_channel_sending_node_checks_part2() { + let nodes = create_network(2); + + // BOLT #2 spec: Sending node must set funding_satoshis to less than 2^24 satoshis + let channel_value_satoshis=2^24; + let push_msat=10001; + assert!(nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42).is_err()); + + // BOLT #2 spec: Sending node must set push_msat to equal or less than 1000 * funding_satoshis + let channel_value_satoshis=10000; + // Test when push_msat is equal to 1000 * funding_satoshis. + let push_msat=1000*channel_value_satoshis+1; + assert!(nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42).is_err()); + + // BOLT #2 spec: Sending node must set set channel_reserve_satoshis greater than or equal to dust_limit_satoshis + let channel_value_satoshis=10000; + let push_msat=10001; + assert!(nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, push_msat, 42).is_ok()); //Create a valid channel + let node0_to_1_send_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id()); + assert!(node0_to_1_send_open_channel.channel_reserve_satoshis>=node0_to_1_send_open_channel.dust_limit_satoshis); + + // BOLT #2 spec: Sending node must set undefined bits in channel_flags to 0 + // Only the least-significant bit of channel_flags is currently defined resulting in channel_flags only having one of two possible states 0 or 1 + assert!(node0_to_1_send_open_channel.channel_flags<=1); + + // BOLT #2 spec: Sending node should set to_self_delay sufficient to ensure the sender can irreversibly spend a commitment transaction output, in case of misbehaviour by the receiver. + assert!(BREAKDOWN_TIMEOUT>0); + assert!(node0_to_1_send_open_channel.to_self_delay==BREAKDOWN_TIMEOUT); + + // BOLT #2 spec: Sending node must ensure the chain_hash value identifies the chain it wishes to open the channel within. + let chain_hash=genesis_block(Network::Testnet).header.bitcoin_hash(); + assert_eq!(node0_to_1_send_open_channel.chain_hash,chain_hash); + + // BOLT #2 spec: Sending node must set funding_pubkey, revocation_basepoint, htlc_basepoint, payment_basepoint, and delayed_payment_basepoint to valid DER-encoded, compressed, secp256k1 pubkeys. + assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.funding_pubkey.serialize()).is_ok()); + assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.revocation_basepoint.serialize()).is_ok()); + assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.htlc_basepoint.serialize()).is_ok()); + assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.payment_basepoint.serialize()).is_ok()); + assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.delayed_payment_basepoint.serialize()).is_ok()); +} + // BOLT 2 Requirements for the Sender when constructing and sending an update_add_htlc message. // BOLT 2 Requirement: MUST NOT offer amount_msat it cannot pay for in the remote commitment transaction at the current feerate_per_kw (see "Updating Fees") while maintaining its channel reserve. //TODO: I don't believe this is explicitly enforced when sending an HTLC but as the Fee aspect of the BOLT specs is in flux leaving this as a TODO. diff --git a/src/util/rng.rs b/src/util/rng.rs index ae50154d9..63fbc99b5 100644 --- a/src/util/rng.rs +++ b/src/util/rng.rs @@ -7,12 +7,6 @@ mod real_rng { rng.fill_bytes(data); } - pub fn rand_u832() -> [u8; 32] { - let mut res = [0; 32]; - fill_bytes(&mut res); - res - } - pub fn rand_f32() -> f32 { let mut rng = thread_rng(); rng.next_f32() @@ -37,17 +31,6 @@ mod fuzzy_rng { data[off..].copy_from_slice(&byte_utils::be64_to_array(rng)[0..rem]); } - pub fn rand_u832() -> [u8; 32] { - let rng = unsafe { RNG_ITER += 1; RNG_ITER - 1 }; - let mut res = [0; 32]; - let data = byte_utils::le64_to_array(rng); - res[8*0..8*1].copy_from_slice(&data); - res[8*1..8*2].copy_from_slice(&data); - res[8*2..8*3].copy_from_slice(&data); - res[8*3..8*4].copy_from_slice(&data); - res - } - pub fn rand_f32() -> f32 { let rng = unsafe { RNG_ITER += 1; RNG_ITER - 1 }; f64::from_bits(rng) as f32 diff --git a/src/util/test_utils.rs b/src/util/test_utils.rs index b889e254e..4360c5701 100644 --- a/src/util/test_utils.rs +++ b/src/util/test_utils.rs @@ -215,6 +215,7 @@ impl Logger for TestLogger { pub struct TestKeysInterface { backing: keysinterface::KeysManager, pub override_session_priv: Mutex>, + pub override_channel_id_priv: Mutex>, } impl keysinterface::KeysInterface for TestKeysInterface { @@ -229,6 +230,13 @@ impl keysinterface::KeysInterface for TestKeysInterface { None => self.backing.get_session_key() } } + + fn get_channel_id(&self) -> [u8; 32] { + match *self.override_channel_id_priv.lock().unwrap() { + Some(key) => key.clone(), + None => self.backing.get_channel_id() + } + } } impl TestKeysInterface { @@ -236,6 +244,7 @@ impl TestKeysInterface { Self { backing: keysinterface::KeysManager::new(seed, network, logger), override_session_priv: Mutex::new(None), + override_channel_id_priv: Mutex::new(None), } } } -- 2.39.5