sigs: (Signature, Signature),
preimage: Option<PaymentPreimage>,
amount: u64,
+ },
+ Funding {
+ channel_value: u64,
}
}
sigs.1.write(writer)?;
preimage.write(writer)?;
writer.write_all(&byte_utils::be64_to_array(*amount))?;
+ },
+ &InputMaterial::Funding { ref channel_value } => {
+ writer.write_all(&[3; 1])?;
+ channel_value.write(writer)?;
}
}
Ok(())
preimage,
amount
}
+ },
+ 3 => {
+ let channel_value = Readable::read(reader)?;
+ InputMaterial::Funding {
+ channel_value
+ }
}
_ => return Err(DecodeError::InvalidValue),
};
// Used just for ChannelManager to make sure it has the latest channel data during
// deserialization
current_remote_commitment_number: u64,
+ // Used just for ChannelManager to make sure it has the latest channel data during
+ // deserialization
+ current_local_commitment_number: u64,
payment_preimages: HashMap<PaymentHash, PaymentPreimage>,
self.remote_hash_commitment_number != other.remote_hash_commitment_number ||
self.prev_local_signed_commitment_tx != other.prev_local_signed_commitment_tx ||
self.current_remote_commitment_number != other.current_remote_commitment_number ||
+ self.current_local_commitment_number != other.current_local_commitment_number ||
self.current_local_signed_commitment_tx != other.current_local_signed_commitment_tx ||
self.payment_preimages != other.payment_preimages ||
self.pending_htlcs_updated != other.pending_htlcs_updated ||
writer.write_all(&byte_utils::be48_to_array(0))?;
}
+ if for_local_storage {
+ writer.write_all(&byte_utils::be48_to_array(self.current_local_commitment_number))?;
+ } else {
+ writer.write_all(&byte_utils::be48_to_array(0))?;
+ }
+
writer.write_all(&byte_utils::be64_to_array(self.payment_preimages.len() as u64))?;
for payment_preimage in self.payment_preimages.values() {
writer.write_all(&payment_preimage.0[..])?;
onchain_detection: onchain_detection,
their_htlc_base_key: Some(their_htlc_base_key.clone()),
their_delayed_payment_base_key: Some(their_delayed_payment_base_key.clone()),
- funding_redeemscript: Some(funding_redeemscript),
+ funding_redeemscript: Some(funding_redeemscript.clone()),
channel_value_satoshis: Some(channel_value_satoshis),
their_cur_revocation_points: None,
prev_local_signed_commitment_tx: None,
current_local_signed_commitment_tx: None,
current_remote_commitment_number: 1 << 48,
+ current_local_commitment_number: 0xffff_ffff_ffff,
payment_preimages: HashMap::new(),
pending_htlcs_updated: Vec::new(),
onchain_events_waiting_threshold_conf: HashMap::new(),
outputs_to_watch: HashMap::new(),
- onchain_tx_handler: OnchainTxHandler::new(destination_script.clone(), keys, logger.clone()),
+ onchain_tx_handler: OnchainTxHandler::new(destination_script.clone(), keys, funding_redeemscript, logger.clone()),
last_block_hash: Default::default(),
secp_ctx: Secp256k1::new(),
if self.their_to_self_delay.is_none() {
return Err(MonitorUpdateError("Got a local commitment tx info update before we'd set basic information about the channel"));
}
+ // Returning a monitor error before updating tracking points means in case of using
+ // a concurrent watchtower implementation for same channel, if this one doesn't
+ // reject update as we do, you MAY have the latest local valid commitment tx onchain
+ // for which you want to spend outputs. We're NOT robust again this scenario right
+ // now but we should consider it later.
+ if let Err(_) = self.onchain_tx_handler.provide_latest_local_tx(commitment_tx.clone()) {
+ return Err(MonitorUpdateError("Local commitment signed has already been signed, no further update of LOCAL commitment transaction is allowed"));
+ }
+ self.current_local_commitment_number = 0xffff_ffff_ffff - ((((commitment_tx.without_valid_witness().input[0].sequence as u64 & 0xffffff) << 3*8) | (commitment_tx.without_valid_witness().lock_time as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor);
self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take();
self.current_local_signed_commitment_tx = Some(LocalSignedTx {
txid: commitment_tx.txid(),
}
pub(super) fn get_cur_local_commitment_number(&self) -> u64 {
- if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
- 0xffff_ffff_ffff - ((((local_tx.tx.without_valid_witness().input[0].sequence as u64 & 0xffffff) << 3*8) | (local_tx.tx.without_valid_witness().lock_time as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor)
- } else { 0xffff_ffff_ffff }
+ self.current_local_commitment_number
}
/// Attempts to claim a remote commitment transaction's outputs using the revocation key and
(claimable_outpoints, Some((htlc_txid, tx.output.clone())))
}
- fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx) -> (Vec<Transaction>, Vec<TxOut>, Option<(Script, SecretKey, Script)>) {
+ fn broadcast_by_local_state(&self, commitment_tx: &Transaction, local_tx: &LocalSignedTx) -> (Vec<Transaction>, Vec<TxOut>, Option<(Script, SecretKey, Script)>) {
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
Err(_) => continue,
};
- let mut per_input_material = HashMap::with_capacity(1);
- per_input_material.insert(htlc_timeout_tx.input[0].previous_output, InputMaterial::LocalHTLC { witness_script: htlc_script, sigs: (*their_sig, our_sig), preimage: None, amount: htlc.amount_msat / 1000});
- //TODO: with option_simplified_commitment track outpoint too
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_timeout_tx.input[0].previous_output.vout, htlc_timeout_tx.input[0].previous_output.txid);
res.push(htlc_timeout_tx);
} else {
Err(_) => continue,
};
- let mut per_input_material = HashMap::with_capacity(1);
- per_input_material.insert(htlc_success_tx.input[0].previous_output, InputMaterial::LocalHTLC { witness_script: htlc_script, sigs: (*their_sig, our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000});
- //TODO: with option_simplified_commitment track outpoint too
log_trace!(self, "Outpoint {}:{} is being being claimed", htlc_success_tx.input[0].previous_output.vout, htlc_success_tx.input[0].previous_output.txid);
res.push(htlc_success_tx);
}
}
- watch_outputs.push(local_tx.tx.without_valid_witness().output[transaction_output_index as usize].clone());
+ watch_outputs.push(commitment_tx.output[transaction_output_index as usize].clone());
} else { panic!("Should have sigs for non-dust local tx outputs!") }
}
}
if local_tx.txid == commitment_txid {
is_local_tx = true;
log_trace!(self, "Got latest local commitment tx broadcast, searching for available HTLCs to claim");
- let mut res = self.broadcast_by_local_state(local_tx);
+ let mut res = self.broadcast_by_local_state(tx, local_tx);
append_onchain_update!(res);
}
}
if local_tx.txid == commitment_txid {
is_local_tx = true;
log_trace!(self, "Got previous local commitment tx broadcast, searching for available HTLCs to claim");
- assert!(local_tx.tx.has_local_sig());
- let mut res = self.broadcast_by_local_state(local_tx);
+ let mut res = self.broadcast_by_local_state(tx, local_tx);
append_onchain_update!(res);
}
}
/// out-of-band the other node operator to coordinate with him if option is available to you.
/// In any-case, choice is up to the user.
pub fn get_latest_local_commitment_txn(&mut self) -> Vec<Transaction> {
- // TODO: We should likely move all of the logic in here into OnChainTxHandler and unify it
- // to ensure add_local_sig is only ever called once no matter what. This likely includes
- // tracking state and panic!()ing if we get an update after force-closure/local-tx signing.
log_trace!(self, "Getting signed latest local commitment transaction!");
- if let &mut Some(ref mut local_tx) = &mut self.current_local_signed_commitment_tx {
- self.onchain_detection.keys.sign_local_commitment(&mut local_tx.tx, self.funding_redeemscript.as_ref().unwrap(), self.channel_value_satoshis.unwrap(), &self.secp_ctx);
+ if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_local_tx(self.channel_value_satoshis.unwrap()) {
+ let mut res = vec![commitment_tx];
+ if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
+ let mut htlc_txn = self.broadcast_by_local_state(res.get(0).unwrap(), local_tx).0;
+ res.append(&mut htlc_txn);
+ // We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
+ // The data will be re-generated and tracked in check_spend_local_transaction if we get a confirmation.
+ }
+ return res
}
- if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
- let mut res = vec![local_tx.tx.with_valid_witness().clone()];
- res.append(&mut self.broadcast_by_local_state(local_tx).0);
- // We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
- // The data will be re-generated and tracked in check_spend_local_transaction if we get a confirmation.
- res
- } else {
- Vec::new()
+ Vec::new()
+ }
+
+ /// Unsafe test-only version of get_latest_local_commitment_txn used by our test framework
+ /// to bypass LocalCommitmentTransaction state update lockdown after signature and generate
+ /// revoked commitment transaction.
+ #[cfg(test)]
+ pub fn unsafe_get_latest_local_commitment_txn(&mut self) -> Vec<Transaction> {
+ log_trace!(self, "Getting signed copy of latest local commitment transaction!");
+ if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_copy_local_tx(self.channel_value_satoshis.unwrap()) {
+ let mut res = vec![commitment_tx];
+ if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
+ let mut htlc_txn = self.broadcast_by_local_state(res.get(0).unwrap(), local_tx).0;
+ res.append(&mut htlc_txn);
+ }
+ return res
}
+ Vec::new()
}
/// Called by SimpleManyChannelMonitor::block_connected, which implements
let should_broadcast = if let Some(_) = self.current_local_signed_commitment_tx {
self.would_broadcast_at_height(height)
} else { false };
- if let Some(ref mut cur_local_tx) = self.current_local_signed_commitment_tx {
- if should_broadcast {
- self.onchain_detection.keys.sign_local_commitment(&mut cur_local_tx.tx, self.funding_redeemscript.as_ref().unwrap(), self.channel_value_satoshis.unwrap(), &mut self.secp_ctx);
- }
+ if should_broadcast {
+ claimable_outpoints.push(ClaimRequest { absolute_timelock: height, aggregable: false, outpoint: BitcoinOutPoint { txid: self.onchain_detection.funding_info.as_ref().unwrap().0.txid.clone(), vout: self.onchain_detection.funding_info.as_ref().unwrap().0.index as u32 }, witness_data: InputMaterial::Funding { channel_value: self.channel_value_satoshis.unwrap() }});
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
if should_broadcast {
- log_trace!(self, "Broadcast onchain {}", log_tx!(cur_local_tx.tx.with_valid_witness()));
- broadcaster.broadcast_transaction(&cur_local_tx.tx.with_valid_witness());
- let (txs, new_outputs, _) = self.broadcast_by_local_state(&cur_local_tx);
- if !new_outputs.is_empty() {
- watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
- }
- for tx in txs {
- log_trace!(self, "Broadcast onchain {}", log_tx!(tx));
- broadcaster.broadcast_transaction(&tx);
+ if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_local_tx(self.channel_value_satoshis.unwrap()) {
+ let (txs, new_outputs, _) = self.broadcast_by_local_state(&commitment_tx, cur_local_tx);
+ if !new_outputs.is_empty() {
+ watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
+ }
+ for tx in txs {
+ log_trace!(self, "Broadcast onchain {}", log_tx!(tx));
+ broadcaster.broadcast_transaction(&tx);
+ }
}
}
}
LocalSignedTx {
txid: tx.txid(),
- tx, revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw,
+ tx,
+ revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw,
htlc_outputs: htlcs
}
}
};
let current_remote_commitment_number = <U48 as Readable>::read(reader)?.0;
+ let current_local_commitment_number = <U48 as Readable>::read(reader)?.0;
let payment_preimages_len: u64 = Readable::read(reader)?;
let mut payment_preimages = HashMap::with_capacity(cmp::min(payment_preimages_len as usize, MAX_ALLOC_SIZE / 32));
prev_local_signed_commitment_tx,
current_local_signed_commitment_tx,
current_remote_commitment_number,
+ current_local_commitment_number,
payment_preimages,
pending_htlcs_updated,