diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index abc019852..f46a78c60 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -863,6 +863,8 @@ pub struct ChannelActorState { // The commitment point that is going to be used in the following commitment transaction. pub counterparty_commitment_point: Option, pub counterparty_channel_parameters: Option, + pub holder_shutdown_signature: Option, + pub counterparty_shutdown_signature: Option, } #[derive(PartialEq, Eq, Clone, Debug)] @@ -962,7 +964,7 @@ bitflags! { /// we and they have sent `shutdown` messages and all HTLCs are resolved. const THEIR_CLOSING_SIGNED = 1 << 3; /// Indicates all pending HTLCs are resolved, and this channel will be dropped. - const DROPPING_PENDING = ShuttingDownFlags::AWAITING_PENDING_TLCS.bits() | 1 << 2; + const DROPPING_PENDING = ShuttingDownFlags::AWAITING_PENDING_TLCS.bits() | ShuttingDownFlags::OUR_CLOSING_SIGNED.bits() | ShuttingDownFlags::THEIR_CLOSING_SIGNED.bits(); } } @@ -994,6 +996,8 @@ pub enum ChannelState { /// We've successfully negotiated a `closing_signed` dance. At this point, the `ChannelManager` /// is about to drop us, but we store this anyway. ShuttingDown(ShuttingDownFlags), + /// This channel is closed. + Closed, } fn new_channel_id_from_seed(seed: &[u8]) -> Hash256 { @@ -1096,6 +1100,8 @@ impl ChannelActorState { counterparty_nonce: Some(counterparty_nonce), counterparty_commitment_point: Some(counterparty_commitment_point), counterparty_initial_commitment_point: Some(counterparty_prev_commitment_point), + holder_shutdown_signature: None, + counterparty_shutdown_signature: None, } } @@ -1135,6 +1141,8 @@ impl ChannelActorState { counterparty_initial_commitment_point: None, holder_shutdown_script: None, counterparty_shutdown_script: None, + holder_shutdown_signature: None, + counterparty_shutdown_signature: None, } } } @@ -1199,7 +1207,6 @@ impl ChannelActorState { .funding_tx .as_ref() .expect("Funding transaction is present"); - dbg!(&tx); // By convention, the funding tx output for the channel is the first output. tx.output_pts_iter() .next() @@ -1526,6 +1533,74 @@ impl ChannelActorState { Ok(()) } + PCNMessage::ClosingSigned(closing) => { + let flags = match self.state { + ChannelState::ShuttingDown(flags) + if flags.contains(ShuttingDownFlags::AWAITING_PENDING_TLCS) => + { + flags + } + _ => { + return Err(ProcessingChannelError::InvalidState(format!( + "received ClosingSigned message, but we're not ready for ClosingSigned, state is currently {:?}", + self.state + ))); + } + }; + let ClosingSigned { + partial_signature, + channel_id, + fee: _, + } = closing; + + if channel_id != self.get_id() { + return Err(ProcessingChannelError::InvalidParameter( + "Channel id mismatch".to_string(), + )); + } + + let verify_ctx = Musig2VerifyContext::from(&*self); + let tx = self.build_shutdown_tx( + self.get_holder_shutdown_script(), + self.get_counterparty_shutdown_script(), + ); + let message = tx.hash(); + verify_ctx.verify(partial_signature, message.as_slice())?; + debug!("Successfully verified ClosingSigned message"); + self.counterparty_shutdown_signature = Some(partial_signature); + + let flags = flags | ShuttingDownFlags::THEIR_CLOSING_SIGNED; + self.state = ChannelState::ShuttingDown(flags); + if flags.contains(ShuttingDownFlags::DROPPING_PENDING) { + self.state = ChannelState::Closed; + let partial_signatures = if self.should_holder_send_tx_signatures_first() { + [ + self.holder_shutdown_signature.unwrap(), + self.counterparty_shutdown_signature.unwrap(), + ] + } else { + [ + self.counterparty_shutdown_signature.unwrap(), + self.holder_shutdown_signature.unwrap(), + ] + }; + let tx = + aggregate_partial_signatures_for_tx(tx, verify_ctx, partial_signatures) + .expect("The validity of the signatures verified"); + + network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::ChannelClosed( + self.get_id(), + self.peer_id.clone(), + tx, + ), + )) + .expect("network actor alive"); + } + Ok(()) + } + _ => { warn!("Received unsupported message: {:?}", &message); Ok(()) @@ -1561,6 +1636,7 @@ impl ChannelActorState { )) .expect("network actor alive"); + self.holder_shutdown_signature = Some(signature); network .send_message(NetworkActorMessage::new_event( NetworkActorEvent::ChannelShutdown(self.get_id(), self.peer_id.clone()), diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 97edff5f7..f21247a69 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -1,3 +1,4 @@ +use ckb_types::core::TransactionView; use ckb_types::packed::{OutPoint, Transaction}; use log::{debug, error, info, warn}; use ractor::{async_trait as rasync_trait, Actor, ActorCell, ActorProcessingErr, ActorRef}; @@ -96,6 +97,7 @@ pub enum NetworkServiceEvent { ChannelPendingToBeAccepted(PeerId, Hash256), ChannelReady(PeerId, Hash256), ChannelShutDown(PeerId, Hash256), + ChannelClosed(PeerId, Hash256, TransactionView), } /// Events that can be sent to the network actor. Except for NetworkServiceEvent, @@ -115,8 +117,10 @@ pub enum NetworkActorEvent { ChannelAccepted(Hash256, Hash256), /// A channel is ready to use. ChannelReady(Hash256, PeerId), - /// A channel is ready to use. + /// A channel is being shutting down. ChannelShutdown(Hash256, PeerId), + /// A channel is already closed. + ChannelClosed(Hash256, PeerId, TransactionView), /// Both parties are now able to broadcast a valid funding transaction. FundingTransactionPending(Transaction, OutPoint, Hash256), @@ -650,6 +654,20 @@ impl Actor for NetworkActor { )) .expect("myself alive"); } + NetworkActorEvent::ChannelClosed(channel_id, peer_id, tx) => { + info!( + "Channel ({:?}) to peer {:?} is already closed. Closing transaction {:?} can be broacasted now.", + channel_id, peer_id, tx + ); + // Notify outside observers. + myself + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::NetworkServiceEvent( + NetworkServiceEvent::ChannelClosed(peer_id, channel_id, tx), + ), + )) + .expect("myself alive"); + } NetworkActorEvent::PeerMessage(peer_id, session, message) => { self.handle_peer_message(state, peer_id, session, message) .await? diff --git a/src/ckb/serde_utils.rs b/src/ckb/serde_utils.rs index 19bee95a9..c251f7397 100644 --- a/src/ckb/serde_utils.rs +++ b/src/ckb/serde_utils.rs @@ -40,7 +40,7 @@ where String::deserialize(deserializer) .and_then(|string| { - if &string[..2].to_lowercase() != "0x" { + if string.len() < 2 || &string[..2].to_lowercase() != "0x" { return Err(Error::custom("hex string should start with 0x")); }; hex::decode(&string[2..]) diff --git a/src/ckb/types.rs b/src/ckb/types.rs index 0a13cca1c..105619ec6 100644 --- a/src/ckb/types.rs +++ b/src/ckb/types.rs @@ -810,6 +810,7 @@ impl TryFrom for Shutdown { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClosingSigned { pub channel_id: Hash256, + // TODO: fee is actually not used for now. pub fee: u64, pub partial_signature: PartialSignature, }