Merge pull request #118 from TheBlueMatt/2018-08-103-rebased

Rebase of #103
This commit is contained in:
Matt Corallo 2018-08-17 14:00:50 -04:00 committed by GitHub
commit 0f24a67c82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 146 additions and 41 deletions

View file

@ -199,7 +199,10 @@ pub fn do_test(data: &[u8]) {
let mut channel = if get_slice!(1)[0] != 0 {
let chan_value = slice_to_be24(get_slice!(3));
let mut chan = Channel::new_outbound(&fee_est, chan_keys!(), their_pubkey, chan_value, get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)), Arc::clone(&logger));
let mut chan = match Channel::new_outbound(&fee_est, chan_keys!(), their_pubkey, chan_value, slice_to_be24(get_slice!(3)), get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)), Arc::clone(&logger)) {
Ok(chan) => chan,
Err(_) => return,
};
chan.get_open_channel(Sha256dHash::from(get_slice!(32)), &fee_est).unwrap();
let accept_chan = if get_slice!(1)[0] == 0 {
decode_msg_with_len16!(msgs::AcceptChannel, 270, 1)

View file

@ -257,7 +257,8 @@ pub fn do_test(data: &[u8]) {
if !peers.borrow()[peer_id as usize] { return; }
let their_key = get_pubkey!();
let chan_value = slice_to_be24(get_slice!(3)) as u64;
if channelmanager.create_channel(their_key, chan_value, 0).is_err() { return; }
let push_msat_value = slice_to_be24(get_slice!(3)) as u64;
if channelmanager.create_channel(their_key, chan_value, push_msat_value, 0).is_err() { return; }
},
6 => {
let mut channels = channelmanager.list_channels();

View file

@ -24,6 +24,7 @@ use chain::transaction::OutPoint;
use util::{transaction_utils,rng};
use util::sha2::Sha256;
use util::logger::{Logger, Record};
use util::errors::APIError;
use std;
use std::default::Default;
@ -319,7 +320,6 @@ pub struct Channel {
}
const OUR_MAX_HTLCS: u16 = 5; //TODO
const CONF_TARGET: u32 = 12; //TODO: Should be much higher
/// Confirmation count threshold at which we close a channel. Ideally we'd keep the channel around
/// on ice until the funding transaction gets more confirmations, but the LN protocol doesn't
/// really allow for this, so instead we're stuck closing it out at that point.
@ -360,7 +360,8 @@ impl Channel {
/// Guaranteed to return a value no larger than channel_value_satoshis
fn get_our_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
cmp::min(channel_value_satoshis, 1000) //TODO
let (q, _) = channel_value_satoshis.overflowing_div(100);
cmp::min(channel_value_satoshis, cmp::max(q, 1000)) //TODO
}
fn derive_our_dust_limit_satoshis(at_open_background_feerate: u64) -> u64 {
@ -371,16 +372,33 @@ impl Channel {
1000 // TODO
}
// Constructors:
fn derive_minimum_depth(_channel_value_satoshis_msat: u64, _value_to_self_msat: u64) -> u32 {
const CONF_TARGET: u32 = 12; //TODO: Should be much higher
CONF_TARGET
}
/// panics if channel_value_satoshis is >= `MAX_FUNDING_SATOSHIS`
pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, announce_publicly: bool, user_id: u64, logger: Arc<Logger>) -> Channel {
fn derive_maximum_minimum_depth(_channel_value_satoshis_msat: u64, _value_to_self_msat: u64) -> u32 {
const CONF_TARGET: u32 = 12; //TODO: Should be much higher
CONF_TARGET * 2
}
// Constructors:
pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, announce_publicly: bool, user_id: u64, logger: Arc<Logger>) -> Result<Channel, APIError> {
if channel_value_satoshis >= MAX_FUNDING_SATOSHIS {
panic!("funding value > 2^24");
return Err(APIError::APIMisuseError{err: "funding value > 2^24"});
}
if push_msat > channel_value_satoshis * 1000 {
return Err(APIError::APIMisuseError{err: "push value > channel value"});
}
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
if Channel::get_our_channel_reserve_satoshis(channel_value_satoshis) < Channel::derive_our_dust_limit_satoshis(background_feerate) {
return Err(APIError::FeeRateTooHigh{err: format!("Not enough reserve above dust limit can be found at current fee rate({})", background_feerate), feerate: background_feerate});
}
let feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
let secp_ctx = Secp256k1::new();
let our_channel_monitor_claim_key_hash = Hash160::from_data(&PublicKey::from_secret_key(&secp_ctx, &chan_keys.channel_monitor_claim_key).unwrap().serialize());
@ -390,7 +408,7 @@ impl Channel {
&chan_keys.htlc_base_key,
BREAKDOWN_TIMEOUT, our_channel_monitor_claim_script);
Channel {
Ok(Channel {
user_id: user_id,
channel_id: rng::rand_u832(),
@ -403,7 +421,7 @@ impl Channel {
local_keys: chan_keys,
cur_local_commitment_transaction_number: (1 << 48) - 1,
cur_remote_commitment_transaction_number: (1 << 48) - 1,
value_to_self_msat: channel_value_satoshis * 1000, //TODO: give them something on open? Parameterize it?
value_to_self_msat: channel_value_satoshis * 1000 - push_msat,
pending_htlcs: Vec::new(),
holding_cell_htlc_updates: Vec::new(),
next_local_htlc_id: 0,
@ -444,7 +462,7 @@ impl Channel {
channel_monitor: channel_monitor,
logger,
}
})
}
fn check_remote_fee(fee_estimator: &FeeEstimator, feerate_per_kw: u32) -> Result<(), HandleError> {
@ -462,31 +480,43 @@ impl Channel {
/// Generally prefers to take the DisconnectPeer action on failure, as a notice to the sender
/// that we're rejecting the new channel.
pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, require_announce: bool, allow_announce: bool, logger: Arc<Logger>) -> Result<Channel, HandleError> {
macro_rules! return_error_message {
( $msg: expr ) => {
return Err(HandleError{err: $msg, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: $msg.to_string() }})});
}
}
// Check sanity of message fields:
if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
return Err(HandleError{err: "funding value > 2^24", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("funding value > 2^24");
}
if msg.channel_reserve_satoshis > msg.funding_satoshis {
return Err(HandleError{err: "Bogus channel_reserve_satoshis", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("Bogus channel_reserve_satoshis");
}
if msg.push_msat > (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "push_msat more than highest possible value", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("push_msat larger than funding value");
}
if msg.dust_limit_satoshis > msg.funding_satoshis {
return Err(HandleError{err: "Peer never wants payout outputs?", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("Peer never wants payout outputs?");
}
if msg.dust_limit_satoshis > msg.channel_reserve_satoshis {
return_error_message!("Bogus; channel reserve is less than dust limit");
}
if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "Minimum htlc value is full channel value", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("Miminum htlc value is full channel value");
}
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw).map_err(|e|
HandleError{err: e.err, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: e.err.to_string() }})}
)?;
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
return Err(HandleError{err: "They wanted our payments to be delayed by a needlessly long period", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("They wanted our payments to be delayed by a needlessly long period");
}
if msg.max_accepted_htlcs < 1 {
return Err(HandleError{err: "0 max_accpted_htlcs makes for a useless channel", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
return_error_message!("0 max_accpted_htlcs makes for a useless channel");
}
if (msg.channel_flags & 254) != 0 {
return Err(HandleError{err: "unknown channel_flags", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
if msg.max_accepted_htlcs > 483 {
return_error_message!("max_accpted_htlcs > 483");
}
// Convert things into internal flags and prep our state:
@ -501,6 +531,31 @@ impl Channel {
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
let our_dust_limit_satoshis = Channel::derive_our_dust_limit_satoshis(background_feerate);
let our_channel_reserve_satoshis = Channel::get_our_channel_reserve_satoshis(msg.funding_satoshis);
if our_channel_reserve_satoshis < our_dust_limit_satoshis {
return_error_message!("Suitalbe channel reserve not found. aborting");
}
if msg.channel_reserve_satoshis < our_dust_limit_satoshis {
return_error_message!("channel_reserve_satoshis too small");
}
if our_channel_reserve_satoshis < msg.dust_limit_satoshis {
return_error_message!("Dust limit too high for our channel reserve");
}
// check if the funder's amount for the initial commitment tx is sufficient
// for full fee payment
let funders_amount_msat = msg.funding_satoshis * 1000 - msg.push_msat;
if funders_amount_msat < background_feerate * COMMITMENT_TX_BASE_WEIGHT {
return_error_message!("Insufficient funding amount for initial commitment");
}
let to_local_msat = msg.push_msat;
let to_remote_msat = funders_amount_msat - background_feerate * COMMITMENT_TX_BASE_WEIGHT;
if to_local_msat <= msg.channel_reserve_satoshis * 1000 && to_remote_msat <= our_channel_reserve_satoshis * 1000 {
return_error_message!("Insufficient funding amount for initial commitment");
}
let secp_ctx = Secp256k1::new();
let our_channel_monitor_claim_key_hash = Hash160::from_data(&PublicKey::from_secret_key(&secp_ctx, &chan_keys.channel_monitor_claim_key).unwrap().serialize());
let our_channel_monitor_claim_script = Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script();
@ -542,7 +597,7 @@ impl Channel {
feerate_per_kw: msg.feerate_per_kw as u64,
channel_value_satoshis: msg.funding_satoshis,
their_dust_limit_satoshis: msg.dust_limit_satoshis,
our_dust_limit_satoshis: Channel::derive_our_dust_limit_satoshis(background_feerate),
our_dust_limit_satoshis: our_dust_limit_satoshis,
their_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
their_channel_reserve_satoshis: msg.channel_reserve_satoshis,
their_htlc_minimum_msat: msg.htlc_minimum_msat,
@ -1115,29 +1170,57 @@ impl Channel {
// Message handlers:
pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel) -> Result<(), HandleError> {
macro_rules! return_error_message {
( $msg: expr ) => {
return Err(HandleError{err: $msg, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: $msg.to_string() }})});
}
}
// Check sanity of message fields:
if !self.channel_outbound {
return Err(HandleError{err: "Got an accept_channel message from an inbound peer", action: None});
return_error_message!("Got an accept_channel message from an inbound peer");
}
if self.channel_state != ChannelState::OurInitSent as u32 {
return Err(HandleError{err: "Got an accept_channel message at a strange time", action: None});
return_error_message!("Got an accept_channel message at a strange time");
}
if msg.dust_limit_satoshis > 21000000 * 100000000 {
return Err(HandleError{err: "Peer never wants payout outputs?", action: None});
return_error_message!("Peer never wants payout outputs?");
}
if msg.channel_reserve_satoshis > self.channel_value_satoshis {
return Err(HandleError{err: "Bogus channel_reserve_satoshis", action: None});
return_error_message!("Bogus channel_reserve_satoshis");
}
if msg.dust_limit_satoshis > msg.channel_reserve_satoshis {
return_error_message!("Bogus channel_reserve and dust_limit");
}
if msg.channel_reserve_satoshis < self.our_dust_limit_satoshis {
return_error_message!("Peer never wants payout outputs?");
}
if msg.dust_limit_satoshis > Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis) {
return_error_message!("Dust limit is bigger than our channel reverse");
}
if msg.htlc_minimum_msat >= (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "Minimum htlc value is full channel value", action: None});
return_error_message!("Minimum htlc value is full channel value");
}
if msg.minimum_depth > Channel::derive_maximum_minimum_depth(self.channel_value_satoshis*1000, self.value_to_self_msat) {
return_error_message!("minimum_depth too large");
}
//TODO do something with minimum_depth
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
return Err(HandleError{err: "They wanted our payments to be delayed by a needlessly long period", action: None});
return_error_message!("They wanted our payments to be delayed by a needlessly long period");
}
if msg.max_accepted_htlcs < 1 {
return Err(HandleError{err: "0 max_accpted_htlcs makes for a useless channel", action: None});
return_error_message!("0 max_accpted_htlcs makes for a useless channel");
}
if msg.max_accepted_htlcs > 483 {
return_error_message!("max_accpted_htlcs > 483");
}
// TODO: Optional additional constraints mentioned in the spec
// MAY fail the channel if
// funding_satoshi is too small
// htlc_minimum_msat too large
// max_htlc_value_in_flight_msat too small
// channel_reserve_satoshis too large
// max_accepted_htlcs too small
// dust_limit_satoshis too small
self.channel_monitor.set_their_htlc_base_key(&msg.htlc_basepoint);
@ -1955,7 +2038,7 @@ impl Channel {
if header.bitcoin_hash() != self.last_block_connected {
self.last_block_connected = header.bitcoin_hash();
self.funding_tx_confirmations += 1;
if self.funding_tx_confirmations == CONF_TARGET as u64 {
if self.funding_tx_confirmations == Channel::derive_minimum_depth(self.channel_value_satoshis*1000, self.value_to_self_msat) as u64 {
let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 {
self.channel_state |= ChannelState::OurFundingLocked as u32;
true
@ -2023,7 +2106,7 @@ impl Channel {
}
}
if Some(header.bitcoin_hash()) == self.funding_tx_confirmed_in {
self.funding_tx_confirmations = CONF_TARGET as u64 - 1;
self.funding_tx_confirmations = Channel::derive_minimum_depth(self.channel_value_satoshis*1000, self.value_to_self_msat) as u64 - 1;
}
false
}
@ -2031,12 +2114,12 @@ impl Channel {
// Methods to get unprompted messages to send to the remote end (or where we already returned
// something in the handler for the message that prompted this message):
pub fn get_open_channel(&self, chain_hash: Sha256dHash, fee_estimator: &FeeEstimator) -> Result<msgs::OpenChannel, HandleError> {
pub fn get_open_channel(&self, chain_hash: Sha256dHash, fee_estimator: &FeeEstimator) -> Result<msgs::OpenChannel, APIError> {
if !self.channel_outbound {
panic!("Tried to open a channel for an inbound channel?");
}
if self.channel_state != ChannelState::OurInitSent as u32 {
return Err(HandleError{err: "Cannot generate an open_channel after we've moved forward", action: None});
panic!("Cannot generate an open_channel after we've moved forward");
}
if self.cur_local_commitment_transaction_number != (1 << 48) - 1 {
@ -2049,7 +2132,7 @@ impl Channel {
chain_hash: chain_hash,
temporary_channel_id: self.channel_id,
funding_satoshis: self.channel_value_satoshis,
push_msat: 0, //TODO: Something about feerate?
push_msat: self.channel_value_satoshis * 1000 - self.value_to_self_msat,
dust_limit_satoshis: self.our_dust_limit_satoshis,
max_htlc_value_in_flight_msat: Channel::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis),
channel_reserve_satoshis: Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis),
@ -2087,7 +2170,7 @@ impl Channel {
max_htlc_value_in_flight_msat: Channel::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis),
channel_reserve_satoshis: Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis),
htlc_minimum_msat: self.our_htlc_minimum_msat,
minimum_depth: CONF_TARGET,
minimum_depth: Channel::derive_minimum_depth(self.channel_value_satoshis*1000, self.value_to_self_msat),
to_self_delay: BREAKDOWN_TIMEOUT,
max_accepted_htlcs: OUR_MAX_HTLCS,
funding_pubkey: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.funding_key).unwrap(),
@ -2478,7 +2561,7 @@ mod tests {
assert_eq!(PublicKey::from_secret_key(&secp_ctx, &chan_keys.funding_key).unwrap().serialize()[..],
hex::decode("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]);
let mut chan = Channel::new_outbound(&feeest, chan_keys, PublicKey::new(), 10000000, false, 42, Arc::clone(&logger)); // Nothing uses their network key in this test
let mut chan = Channel::new_outbound(&feeest, chan_keys, PublicKey::new(), 10000000, 100000, false, 42, Arc::clone(&logger)).unwrap(); // Nothing uses their network key in this test
chan.their_to_self_delay = 144;
chan.our_dust_limit_satoshis = 546;

View file

@ -21,6 +21,7 @@ use util::{byte_utils, events, internal_traits, rng};
use util::sha2::Sha256;
use util::chacha20poly1305rfc::ChaCha20;
use util::logger::{Logger, Record};
use util::errors::APIError;
use crypto;
use crypto::mac::{Mac,MacResult};
@ -254,7 +255,8 @@ impl ChannelManager {
/// may wish to avoid using 0 for user_id here.
/// If successful, will generate a SendOpenChannel event, so you should probably poll
/// PeerManager::process_events afterwards.
pub fn create_channel(&self, their_network_key: PublicKey, channel_value_satoshis: u64, user_id: u64) -> Result<(), HandleError> {
/// Raises APIError::APIMisuseError when channel_value_satoshis > 2**24 or push_msat being greater than channel_value_satoshis * 1k
pub fn create_channel(&self, their_network_key: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64) -> Result<(), APIError> {
let chan_keys = if cfg!(feature = "fuzztarget") {
ChannelKeys {
funding_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap(),
@ -275,7 +277,7 @@ impl ChannelManager {
}
};
let channel = Channel::new_outbound(&*self.fee_estimator, chan_keys, their_network_key, channel_value_satoshis, self.announce_channels_publicly, user_id, Arc::clone(&self.logger));
let channel = Channel::new_outbound(&*self.fee_estimator, chan_keys, their_network_key, channel_value_satoshis, push_msat, self.announce_channels_publicly, user_id, Arc::clone(&self.logger))?;
let res = channel.get_open_channel(self.genesis_hash.clone(), &*self.fee_estimator)?;
let mut channel_state = self.channel_state.lock().unwrap();
match channel_state.by_id.insert(channel.channel_id(), channel) {
@ -2192,7 +2194,7 @@ mod tests {
static mut CHAN_COUNT: u32 = 0;
fn create_chan_between_nodes(node_a: &Node, node_b: &Node) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) {
node_a.node.create_channel(node_b.node.get_our_node_id(), 100000, 42).unwrap();
node_a.node.create_channel(node_b.node.get_our_node_id(), 100000, 10001, 42).unwrap();
let events_1 = node_a.node.get_and_clear_pending_events();
assert_eq!(events_1.len(), 1);

15
src/util/errors.rs Normal file
View file

@ -0,0 +1,15 @@
use std::fmt;
pub enum APIError {
APIMisuseError {err: &'static str},
FeeRateTooHigh {err: String, feerate: u64},
}
impl fmt::Debug for APIError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
APIError::APIMisuseError {ref err} => f.write_str(err),
APIError::FeeRateTooHigh {ref err, ref feerate} => write!(f, "{} feerate: {}", err, feerate)
}
}
}

View file

@ -1,4 +1,5 @@
pub mod events;
pub mod errors;
pub(crate) mod byte_utils;
pub(crate) mod chacha20poly1305rfc;