const B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT: u64 = 104; // prevout: 40, nSequence: 4, script len: 1, witness lengths: 3/4, sig: 73/4, pubkey: 33/4, output: 31 (TODO: Wrong? Useless?)
#[cfg(not(test))]
-const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
+const COMMITMENT_TX_BASE_WEIGHT: u64 = 896;
#[cfg(test)]
-pub const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
+pub const COMMITMENT_TX_BASE_WEIGHT: u64 = 896;
#[cfg(not(test))]
const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
#[cfg(test)]
pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
+const ANCHOR_VALUE_SATOSHIS: u64 = 330;
+
/// Maximmum `funding_satoshis` value, according to the BOLT #2 specification
/// it's 2^24.
pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
// 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 {
+ if funders_amount_msat < background_feerate * COMMITMENT_TX_BASE_WEIGHT + ANCHOR_VALUE_SATOSHIS * 1000 {
return Err(ChannelError::Close("Insufficient funding amount for initial commitment"));
}
- let to_remote_msat = funders_amount_msat - background_feerate * COMMITMENT_TX_BASE_WEIGHT;
+ let to_remote_msat = funders_amount_msat - background_feerate * COMMITMENT_TX_BASE_WEIGHT - ANCHOR_VALUE_SATOSHIS * 1000;
// While its reasonable for us to not meet the channel reserve initially (if they don't
// want to push much to us), our counterparty should always have more than the reserve.
if to_remote_msat <= remote_channel_reserve_satoshis * 1000 {
let total_fee: u64 = feerate_per_kw * (COMMITMENT_TX_BASE_WEIGHT + (txouts.len() as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
let (value_to_self, value_to_remote) = if self.channel_outbound {
- (value_to_self_msat / 1000 - total_fee as i64, value_to_remote_msat / 1000)
+ (value_to_self_msat / 1000 - total_fee as i64 - ANCHOR_VALUE_SATOSHIS as i64, value_to_remote_msat / 1000)
} else {
- (value_to_self_msat / 1000, value_to_remote_msat / 1000 - total_fee as i64)
+ (value_to_self_msat / 1000, value_to_remote_msat / 1000 - total_fee as i64 - ANCHOR_VALUE_SATOSHIS as i64)
};
let value_to_a = if local { value_to_self } else { value_to_remote };
let value_to_b = if local { value_to_remote } else { value_to_self };
+ if value_to_a >= (dust_limit_satoshis as i64) || !txouts.is_empty() {
+ // Always push the anchor output so that local can get the transaction confirmed.
+ txouts.push((TxOut {
+ script_pubkey: Script::new(), //XXX
+ value: ANCHOR_VALUE_SATOSHIS
+ }, None));
+ }
+
if value_to_a >= (dust_limit_satoshis as i64) {
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
txouts.push((TxOut {
//If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction
if update_fee {
let num_htlcs = local_commitment_tx.1;
- let total_fee: u64 = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
+ let total_fee: u64 = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000 + ANCHOR_VALUE_SATOSHIS;
let remote_reserve_we_require = Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis);
if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + remote_reserve_we_require {
#[test]
fn outbound_commitment_test() {
+ return; // Disabled as we don't have test vectors for the currently-partially-implemented option_anchor_outputs
+
// Test vectors from BOLT 3 Appendix C:
let feeest = TestFeeEstimator{fee_est: 15000};
let logger : Arc<Logger> = Arc::new(test_utils::TestLogger::new());
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
- let channel_value = 1984;
+ let channel_value = 2357;
let push_msat = 800_000;
// First check that any smaller channel_value would result in an error as the funder cannot
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, channel_value, push_msat, InitFeatures::known(), InitFeatures::known());
let channel_id = chan.2;
- let feerate = 255;
+ let feerate = 254;
nodes[0].node.update_fee(channel_id, feerate).unwrap();
check_added_monitors!(nodes[0], 1);
let update_msg = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
{
let commitment_tx = get_local_commitment_txn!(nodes[1], channel_id)[0].clone();
- //We made sure neither party's funds are below the dust limit so -2 non-HTLC txns from number of outputs
- let num_htlcs = commitment_tx.output.len() - 2;
+ //We made sure neither party's funds are below the dust limit so -3 non-HTLC txns from number of outputs
+ let num_htlcs = commitment_tx.output.len() - 3;
let total_fee: u64 = feerate * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
let mut actual_fee = commitment_tx.output.iter().fold(0, |acc, output| acc + output.value);
actual_fee = channel_value - actual_fee;
// Broadcast node 1 commitment txn
let remote_txn = get_local_commitment_txn!(nodes[1], chan_1.2);
- assert_eq!(remote_txn[0].output.len(), 4); // 1 local, 1 remote, 1 htlc inbound, 1 htlc outbound
+ assert_eq!(remote_txn[0].output.len(), 5); // 1 local, 1 remote, 1 anchor, 1 htlc inbound, and 1 htlc outbound
let mut has_both_htlcs = 0; // check htlcs match ones committed
for outp in remote_txn[0].output.iter() {
if outp.value == 800_000 / 1000 {
assert_eq!(revoked_local_txn.len(), 2); // First commitment tx, then HTLC tx
assert_eq!(revoked_local_txn[0].input.len(), 1);
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_5.3.txid());
- assert_eq!(revoked_local_txn[0].output.len(), 2); // Only HTLC and output back to 0 are present
+ assert_eq!(revoked_local_txn[0].output.len(), 3); // Only HTLC, anchor, and output back to 0 are present
assert_eq!(revoked_local_txn[1].input.len(), 1);
assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); // HTLC-Timeout
assert_eq!(revoked_local_txn.len(), 1); // Only commitment tx
assert_eq!(revoked_local_txn[0].input.len(), 1);
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_6.3.txid());
- assert_eq!(revoked_local_txn[0].output.len(), 2); // Only HTLC and output back to A are present
+ assert_eq!(revoked_local_txn[0].output.len(), 3); // Only HTLC, anchor, and output back to A are present
// Revoke the old state
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_4, 3_000_000);
{
// node[0] is gonna to revoke an old state thus node[1] should be able to claim the revoked output
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
assert_eq!(revoked_local_txn.len(), 1);
- // Only output is the full channel value back to nodes[0]:
- assert_eq!(revoked_local_txn[0].output.len(), 1);
+ // Only outputs are the anchor and the full channel value back to nodes[0]:
+ assert_eq!(revoked_local_txn[0].output.len(), 2);
// Send a payment through, updating everyone's latest commitment txn
send_payment(&nodes[0], &vec!(&nodes[1])[..], 5000000, 5_000_000);
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], if no_to_remote { 10_000 } else { 3_000_000 });
// Get the will-be-revoked local txn from nodes[2]
let revoked_local_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
- assert_eq!(revoked_local_txn[0].output.len(), if no_to_remote { 1 } else { 2 });
+ assert_eq!(revoked_local_txn[0].output.len(), if no_to_remote { 1 } else { 3 });
// Revoke the old state
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage, if no_to_remote { 10_000 } else { 3_000_000});
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
check_spends!(node_txn[0], chan.3);
- assert_eq!(node_txn[0].output.len(), 2); // We can't force trimming of to_remote output as channel_reserve_satoshis block us to do so at channel opening
+ assert_eq!(node_txn[0].output.len(), 3); // We can't force trimming of to_remote/anchor outputs as channel_reserve_satoshis block us to do so at channel opening
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0);
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
check_spends!(node_txn[0], chan.3);
- assert_eq!(node_txn[0].output.len(), 2); // We can't force trimming of to_remote output as channel_reserve_satoshis block us to do so at channel opening
+ assert_eq!(node_txn[0].output.len(), 3); // We can't force trimming of to_remote/anchor outputs as channel_reserve_satoshis block us to do so at channel opening
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0);
// Rebalance and check output sanity...
send_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], 500000, 500_000);
send_payment(&nodes[1], &[&nodes[2], &nodes[3], &nodes[5]], 500000, 500_000);
- assert_eq!(get_local_commitment_txn!(nodes[3], chan.2)[0].output.len(), 2);
+ assert_eq!(get_local_commitment_txn!(nodes[3], chan.2)[0].output.len(), 3); // to_local, to_remote, and anchor output
let ds_dust_limit = nodes[3].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().our_dust_limit_satoshis;
// 0th HTLC:
// Double-check that six of the new HTLC were added
// We now have six HTLCs pending over the dust limit and six HTLCs under the dust limit (ie,
- // with to_local and to_remote outputs, 8 outputs and 6 HTLCs not included).
+ // with to_local, to_remote, and anchor outputs, 9 outputs and 6 HTLCs not included).
assert_eq!(get_local_commitment_txn!(nodes[3], chan.2).len(), 1);
- assert_eq!(get_local_commitment_txn!(nodes[3], chan.2)[0].output.len(), 8);
+ assert_eq!(get_local_commitment_txn!(nodes[3], chan.2)[0].output.len(), 9);
// Now fail back three of the over-dust-limit and three of the under-dust-limit payments in one go.
// Fail 0th below-dust, 4th above-dust, 8th above-dust, 10th below-dust HTLCs
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
assert_eq!(node_txn.len(), 1);
check_spends!(node_txn[0], chan.3);
- assert_eq!(node_txn[0].output.len(), 2);
+ assert_eq!(node_txn[0].output.len(), 3);
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()]}, 0);
connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash());
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
let revoked_txn = get_local_commitment_txn!(nodes[0], chan.2);
- // Revoked commitment txn with 4 outputs : to_local, to_remote, 1 outgoing HTLC, 1 incoming HTLC
- assert_eq!(revoked_txn[0].output.len(), 4);
+ // Revoked commitment txn with 5 outputs : to_local, to_remote, anchor, 1 outgoing HTLC, and 1 incoming HTLC
+ assert_eq!(revoked_txn[0].output.len(), 5);
assert_eq!(revoked_txn[0].input.len(), 1);
assert_eq!(revoked_txn[0].input[0].previous_output.txid, chan.3.txid());
let revoked_txid = revoked_txn[0].txid();
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
- // Remote commitment txn with 4 outputs : to_local, to_remote, 1 outgoing HTLC, 1 incoming HTLC
+ // Remote commitment txn with 5 outputs : to_local, to_remote, anchor, 1 outgoing HTLC, and 1 incoming HTLC
let remote_txn = get_local_commitment_txn!(nodes[0], chan.2);
- assert_eq!(remote_txn[0].output.len(), 4);
+ assert_eq!(remote_txn[0].output.len(), 5);
assert_eq!(remote_txn[0].input.len(), 1);
assert_eq!(remote_txn[0].input[0].previous_output.txid, chan.3.txid());
let payment_preimage_1 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3_000_000).0;
let payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3_000_000).0;
- // Remote commitment txn with 4 outputs: to_local, to_remote, 2 outgoing HTLC
+ // Remote commitment txn with 5 outputs: to_local, to_remote, anchor, and 2 outgoing HTLC
let remote_txn = get_local_commitment_txn!(nodes[1], chan.2);
assert_eq!(remote_txn.len(), 3);
- assert_eq!(remote_txn[0].output.len(), 4);
+ assert_eq!(remote_txn[0].output.len(), 5);
assert_eq!(remote_txn[0].input.len(), 1);
assert_eq!(remote_txn[0].input[0].previous_output.txid, chan.3.txid());
check_spends!(remote_txn[1], remote_txn[0]);
// Broadcast node 1 commitment txn to broadcast the HTLC-Timeout
let node_1_commitment_txn = get_local_commitment_txn!(nodes[1], chan_2.2);
assert_eq!(node_1_commitment_txn.len(), 2); // 1 local commitment tx, 1 Outbound HTLC-Timeout
- assert_eq!(node_1_commitment_txn[0].output.len(), 2); // to-self and Offered HTLC (to-remote/to-node-3 is dust)
+ assert_eq!(node_1_commitment_txn[0].output.len(), 3); // to-self, anchor, and Offered HTLC (to-remote/to-node-3 is dust)
check_spends!(node_1_commitment_txn[0], chan_2.3);
check_spends!(node_1_commitment_txn[1], node_1_commitment_txn[0]);
check_closed_broadcast!(nodes[2], false); // We should get a BroadcastChannelUpdate (and *only* a BroadcstChannelUpdate)
let node_2_commitment_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_2_commitment_txn.len(), 3); // ChannelMonitor: 1 offered HTLC-Claim, ChannelManger: 1 local commitment tx, 1 Received HTLC-Claim
- assert_eq!(node_2_commitment_txn[1].output.len(), 2); // to-remote and Received HTLC (to-self is dust)
+ assert_eq!(node_2_commitment_txn[1].output.len(), 3); // to-remote, anchor, and Received HTLC (to-self is dust)
check_spends!(node_2_commitment_txn[1], chan_2.3);
check_spends!(node_2_commitment_txn[2], node_2_commitment_txn[1]);
check_spends!(node_2_commitment_txn[0], node_1_commitment_txn[0]);
// Broadcast node 2 commitment txn
let node_2_commitment_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
assert_eq!(node_2_commitment_txn.len(), 2); // 1 local commitment tx, 1 Received HTLC-Claim
- assert_eq!(node_2_commitment_txn[0].output.len(), 2); // to-remote and Received HTLC (to-self is dust)
+ assert_eq!(node_2_commitment_txn[0].output.len(), 3); // to-remote, anchor, and Received HTLC (to-self is dust)
check_spends!(node_2_commitment_txn[0], chan_2.3);
check_spends!(node_2_commitment_txn[1], node_2_commitment_txn[0]);
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_2_commitment_txn[0].clone()] }, CHAN_CONFIRM_DEPTH + 1);
let node_1_commitment_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_1_commitment_txn.len(), 3); // ChannelMonitor: 1 offered HTLC-Timeout, ChannelManger: 1 local commitment tx, 1 Offered HTLC-Timeout
- assert_eq!(node_1_commitment_txn[1].output.len(), 2); // to-local and Offered HTLC (to-remote is dust)
+ assert_eq!(node_1_commitment_txn[1].output.len(), 3); // to-local, anchor, and Offered HTLC (to-remote is dust)
check_spends!(node_1_commitment_txn[1], chan_2.3);
check_spends!(node_1_commitment_txn[2], node_1_commitment_txn[1]);
check_spends!(node_1_commitment_txn[0], node_2_commitment_txn[0]);