+
+ pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) -> Result<(), ()> {
+ // To prevent any unsafe state discrepancy between offchain and onchain, once local
+ // commitment transaction has been signed due to an event (either block height for
+ // HTLC-timeout or channel force-closure), don't allow any further update of local
+ // commitment transaction view to avoid delivery of revocation secret to counterparty
+ // for the aformentionned signed transaction.
+ if self.local_htlc_sigs.is_some() || self.prev_local_htlc_sigs.is_some() {
+ return Err(());
+ }
+ self.prev_local_commitment = self.local_commitment.take();
+ self.local_commitment = Some(tx);
+ Ok(())
+ }
+
+ fn sign_latest_local_htlcs(&mut self) {
+ if let Some(ref local_commitment) = self.local_commitment {
+ if let Ok(sigs) = self.key_storage.sign_local_commitment_htlc_transactions(local_commitment, self.local_csv, &self.secp_ctx) {
+ self.local_htlc_sigs = Some(Vec::new());
+ let ret = self.local_htlc_sigs.as_mut().unwrap();
+ for (htlc_idx, (local_sig, &(ref htlc, _))) in sigs.iter().zip(local_commitment.per_htlc.iter()).enumerate() {
+ if let Some(tx_idx) = htlc.transaction_output_index {
+ if ret.len() <= tx_idx as usize { ret.resize(tx_idx as usize + 1, None); }
+ ret[tx_idx as usize] = Some((htlc_idx, local_sig.expect("Did not receive a signature for a non-dust HTLC")));
+ } else {
+ assert!(local_sig.is_none(), "Received a signature for a dust HTLC");
+ }
+ }
+ }
+ }
+ }
+ fn sign_prev_local_htlcs(&mut self) {
+ if let Some(ref local_commitment) = self.prev_local_commitment {
+ if let Ok(sigs) = self.key_storage.sign_local_commitment_htlc_transactions(local_commitment, self.local_csv, &self.secp_ctx) {
+ self.prev_local_htlc_sigs = Some(Vec::new());
+ let ret = self.prev_local_htlc_sigs.as_mut().unwrap();
+ for (htlc_idx, (local_sig, &(ref htlc, _))) in sigs.iter().zip(local_commitment.per_htlc.iter()).enumerate() {
+ if let Some(tx_idx) = htlc.transaction_output_index {
+ if ret.len() <= tx_idx as usize { ret.resize(tx_idx as usize + 1, None); }
+ ret[tx_idx as usize] = Some((htlc_idx, local_sig.expect("Did not receive a signature for a non-dust HTLC")));
+ } else {
+ assert!(local_sig.is_none(), "Received a signature for a dust HTLC");
+ }
+ }
+ }
+ }
+ }
+
+ //TODO: getting lastest local transactions should be infaillible and result in us "force-closing the channel", but we may
+ // have empty local commitment transaction if a ChannelMonitor is asked to force-close just after Channel::get_outbound_funding_created,
+ // before providing a initial commitment transaction. For outbound channel, init ChannelMonitor at Channel::funding_signed, there is nothing
+ // to monitor before.
+ pub(super) fn get_fully_signed_local_tx(&mut self, funding_redeemscript: &Script) -> Option<Transaction> {
+ if let Some(ref mut local_commitment) = self.local_commitment {
+ match self.key_storage.sign_local_commitment(local_commitment, &self.secp_ctx) {
+ Ok(sig) => Some(local_commitment.add_local_sig(funding_redeemscript, sig)),
+ Err(_) => return None,
+ }
+ } else {
+ None
+ }
+ }
+
+ #[cfg(test)]
+ pub(super) fn get_fully_signed_copy_local_tx(&mut self, funding_redeemscript: &Script) -> Option<Transaction> {
+ if let Some(ref mut local_commitment) = self.local_commitment {
+ let local_commitment = local_commitment.clone();
+ match self.key_storage.sign_local_commitment(&local_commitment, &self.secp_ctx) {
+ Ok(sig) => Some(local_commitment.add_local_sig(funding_redeemscript, sig)),
+ Err(_) => return None,
+ }
+ } else {
+ None
+ }
+ }
+
+ pub(super) fn get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
+ let mut htlc_tx = None;
+ if self.local_commitment.is_some() {
+ let commitment_txid = self.local_commitment.as_ref().unwrap().txid();
+ if commitment_txid == outp.txid {
+ self.sign_latest_local_htlcs();
+ if let &Some(ref htlc_sigs) = &self.local_htlc_sigs {
+ let &(ref htlc_idx, ref htlc_sig) = htlc_sigs[outp.vout as usize].as_ref().unwrap();
+ htlc_tx = Some(self.local_commitment.as_ref().unwrap()
+ .get_signed_htlc_tx(*htlc_idx, htlc_sig, preimage, self.local_csv));
+ }
+ }
+ }
+ if self.prev_local_commitment.is_some() {
+ let commitment_txid = self.prev_local_commitment.as_ref().unwrap().txid();
+ if commitment_txid == outp.txid {
+ self.sign_prev_local_htlcs();
+ if let &Some(ref htlc_sigs) = &self.prev_local_htlc_sigs {
+ let &(ref htlc_idx, ref htlc_sig) = htlc_sigs[outp.vout as usize].as_ref().unwrap();
+ htlc_tx = Some(self.prev_local_commitment.as_ref().unwrap()
+ .get_signed_htlc_tx(*htlc_idx, htlc_sig, preimage, self.local_csv));
+ }
+ }
+ }
+ htlc_tx
+ }
+
+ #[cfg(test)]
+ pub(super) fn unsafe_get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
+ let latest_had_sigs = self.local_htlc_sigs.is_some();
+ let prev_had_sigs = self.prev_local_htlc_sigs.is_some();
+ let ret = self.get_fully_signed_htlc_tx(outp, preimage);
+ if !latest_had_sigs {
+ self.local_htlc_sigs = None;
+ }
+ if !prev_had_sigs {
+ self.prev_local_htlc_sigs = None;
+ }
+ ret
+ }