From 7d50be17a926f5312dac413e001acba8fceb58f6 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 | 6 +++ src/chain/keysinterface.rs | 21 +++++++++ src/ln/channel.rs | 8 +++- src/ln/functional_tests.rs | 64 +++++++++++++++++++++++++- src/util/test_utils.rs | 9 ++++ 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs index 067c73d217e..36b2d3581cc 100644 --- a/fuzz/fuzz_targets/full_stack_target.rs +++ b/fuzz/fuzz_targets/full_stack_target.rs @@ -281,6 +281,12 @@ 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); + channel_id + } } #[inline] diff --git a/src/chain/keysinterface.rs b/src/chain/keysinterface.rs index 53191faf2c9..fb7513ba018 100644 --- a/src/chain/keysinterface.rs +++ b/src/chain/keysinterface.rs @@ -80,6 +80,8 @@ 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 channel id + fn get_channel_id(&self) -> [u8; 32]; } /// Set of lightning keys needed to operate a channel as described in BOLT 3 @@ -124,6 +126,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 +155,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 +165,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 +253,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 77ea03994a8..93fad343657 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, @@ -3958,6 +3961,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 6a2b6d3db71..b6463bf32d9 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/test_utils.rs b/src/util/test_utils.rs index b04f728b0f7..a51273ffab2 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), } } }