From 91739596167a6638ab55e3b0c6c526bc245d611f Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 14:32:41 +0800 Subject: [PATCH 01/30] Create a mock chain actor to validate transactions --- src/ckb/chain.rs | 74 ++++++++++++++++++++++-------------------- src/ckb/test_utils.rs | 11 ++++--- src/ckb_chain/actor.rs | 54 +++++++++++++++++++----------- 3 files changed, 79 insertions(+), 60 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index 7ec54df0..f64c21a7 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -104,45 +104,47 @@ impl MockContext { } } + pub fn new() -> Self { + let mut context = Context::default(); + + let (map, cell_deps) = Self::get_contract_binaries().into_iter().fold( + (HashMap::new(), vec![]), + |(mut map, mut cell_deps), (contract, binary)| { + let out_point = context.deploy_cell(binary); + let script = context + .build_script(&out_point, Default::default()) + .expect("valid script"); + map.insert(contract, script); + cell_deps.push( + CellDep::new_builder() + .out_point(out_point) + .dep_type(DepType::Code.into()) + .build(), + ); + (map, cell_deps) + }, + ); + let cell_dep_vec = cell_deps.pack(); + debug!("Loaded contracts into the mock environement: {:?}", &map); + debug!( + "Use these contracts by specifying cell deps to {:?}", + &cell_dep_vec + ); + + let context = MockContext { + context: RwLock::new(context), + contracts_context: Arc::new(ContractsContext { + contract_default_scripts: map, + cell_deps: cell_dep_vec, + }), + }; + context + } + // This is used temporarily to test the functionality of the contract. pub fn get() -> &'static Self { static INSTANCE: OnceCell = OnceCell::new(); - INSTANCE.get_or_init(|| { - let mut context = Context::default(); - - let (map, cell_deps) = Self::get_contract_binaries().into_iter().fold( - (HashMap::new(), vec![]), - |(mut map, mut cell_deps), (contract, binary)| { - let out_point = context.deploy_cell(binary); - let script = context - .build_script(&out_point, Default::default()) - .expect("valid script"); - map.insert(contract, script); - cell_deps.push( - CellDep::new_builder() - .out_point(out_point) - .dep_type(DepType::Code.into()) - .build(), - ); - (map, cell_deps) - }, - ); - let cell_dep_vec = cell_deps.pack(); - debug!("Loaded contracts into the mock environement: {:?}", &map); - debug!( - "Use these contracts by specifying cell deps to {:?}", - &cell_dep_vec - ); - - let context = MockContext { - context: RwLock::new(context), - contracts_context: Arc::new(ContractsContext { - contract_default_scripts: map, - cell_deps: cell_dep_vec, - }), - }; - context - }); + INSTANCE.get_or_init(|| Self::new()); INSTANCE.get().unwrap() } } diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index 5b6c5740..38f8ea22 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -109,16 +109,17 @@ impl NetworkNode { let mut chain_base_dir = PathBuf::from(base_dir.as_ref()); chain_base_dir.push("ckb-chain"); - let noop_chain_actor = Actor::spawn_linked(None, MockChainActor {}, (), root.get_cell()) - .await - .expect("start mock chain actor") - .0; + let mock_chain_actor = + Actor::spawn_linked(None, MockChainActor::new(), (), root.get_cell()) + .await + .expect("start mock chain actor") + .0; let network_actor = Actor::spawn_linked( Some(format!("network actor at {:?}", base_dir.as_ref())), NetworkActor::new( event_sender, - noop_chain_actor.clone(), + mock_chain_actor.clone(), MemoryStore::default(), ), (ckb_config, new_tokio_task_tracker()), diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 585c9618..6171d77d 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -214,29 +214,45 @@ impl CkbChainState { } #[cfg(test)] -pub struct MockChainActor; +pub use test_utils::MockChainActor; #[cfg(test)] -#[ractor::async_trait] -impl Actor for MockChainActor { - type Msg = CkbChainMessage; - type State = (); - type Arguments = (); +mod test_utils { + use super::CkbChainMessage; + use crate::ckb::chain::MockContext; - async fn pre_start( - &self, - _: ActorRef, - _: Self::Arguments, - ) -> Result { - Ok(()) + use ractor::{Actor, ActorProcessingErr, ActorRef}; + + pub type MockChainActorState = MockContext; + pub struct MockChainActor {} + + impl MockChainActor { + pub fn new() -> Self { + Self {} + } } - async fn handle( - &self, - _: ActorRef, - _: Self::Msg, - _: &mut Self::State, - ) -> Result<(), ActorProcessingErr> { - Ok(()) + #[ractor::async_trait] + impl Actor for MockChainActor { + type Msg = CkbChainMessage; + type State = MockChainActorState; + type Arguments = (); + + async fn pre_start( + &self, + _: ActorRef, + _: Self::Arguments, + ) -> Result { + Ok(Self::State::new()) + } + + async fn handle( + &self, + _: ActorRef, + _: Self::Msg, + _: &mut Self::State, + ) -> Result<(), ActorProcessingErr> { + Ok(()) + } } } From 62b850798ffcf728ac8729971700099319ede718 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 15:01:37 +0800 Subject: [PATCH 02/30] Add more state to the mock chain actor --- src/ckb_chain/actor.rs | 83 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 6171d77d..d56e6c4c 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -218,12 +218,29 @@ pub use test_utils::MockChainActor; #[cfg(test)] mod test_utils { + use std::collections::HashMap; + use super::CkbChainMessage; use crate::ckb::chain::MockContext; + use ckb_types::packed::Byte32; + use log::{debug, error}; use ractor::{Actor, ActorProcessingErr, ActorRef}; - pub type MockChainActorState = MockContext; + pub struct MockChainActorState { + ctx: MockContext, + committed_tx_status: HashMap, + } + + impl MockChainActorState { + pub fn new() -> Self { + Self { + ctx: MockContext::new(), + committed_tx_status: HashMap::new(), + } + } + } + pub struct MockChainActor {} impl MockChainActor { @@ -248,10 +265,68 @@ mod test_utils { async fn handle( &self, - _: ActorRef, - _: Self::Msg, - _: &mut Self::State, + myself: ActorRef, + message: Self::Msg, + ctx: &mut Self::State, ) -> Result<(), ActorProcessingErr> { + use CkbChainMessage::*; + match message { + Fund(tx, request, reply_port) => { + // TODO: Fill in transaction from request. + let fulfilled_tx = tx.clone(); + debug!( + "fulfilling funding request: request: {:?}, original tx: {:?}, fulfilled tx: {:?}", + request, &tx, &fulfilled_tx + ); + if let Err(e) = reply_port.send(Ok(fulfilled_tx)) { + error!( + "[{}] send reply failed: {:?}", + myself.get_name().unwrap_or_default(), + e + ); + } + } + Sign(tx, reply_port) => { + // TODO: Fill in transaction from request. + let signed_tx = tx.clone(); + debug!( + "signing transaction: original tx: {:?}, signed tx: {:?}", + &tx, &signed_tx + ); + if let Err(e) = reply_port.send(Ok(signed_tx)) { + error!( + "[{}] send reply failed: {:?}", + myself.get_name().unwrap_or_default(), + e + ); + } + } + SendTx(tx) => { + debug!("sending transaction: {:?}", tx); + // TODO: verify the transaction and set the relevant status. + let status = ckb_jsonrpc_types::Status::Committed; + debug!("Verified transaction: {:?}, status: {:?}", tx, status); + ctx.committed_tx_status.insert(tx.hash(), status); + } + TraceTx(tx, reply_port) => { + let status = ctx + .committed_tx_status + .get(&tx.tx_hash) + .cloned() + .unwrap_or(ckb_jsonrpc_types::Status::Unknown); + debug!( + "tracing transaction: {:?}, status: {:?}", + &tx.tx_hash, &status + ); + if let Err(e) = reply_port.send(status) { + error!( + "[{}] send reply failed: {:?}", + myself.get_name().unwrap_or_default(), + e + ); + } + } + } Ok(()) } } From 34317e4165b8102061df80bbe7404f985bb94d06 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 15:36:12 +0800 Subject: [PATCH 03/30] Add a unit test to create a channel --- src/ckb/channel.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index d6553898..5f15b818 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -3403,6 +3403,7 @@ mod tests { use crate::{ ckb::{ + channel::{ChannelCommand, ChannelCommandWithId}, network::{AcceptChannelCommand, OpenChannelCommand}, test_utils::NetworkNode, NetworkActorCommand, NetworkActorMessage, @@ -3676,6 +3677,112 @@ mod tests { .await; } + #[tokio::test] + async fn test_create_channel() { + + let [mut node_a, mut node_b] = NetworkNode::new_n_interconnected_nodes(2) + .await + .try_into() + .unwrap(); + + node_a + .network_actor + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::OpenChannel( + OpenChannelCommand { + peer_id: node_b.peer_id.clone(), + funding_amount: 1000, + }, + None, + ), + )) + .expect("node_a alive"); + let old_channel_id = node_b + .expect_to_process_event(|event| match event { + NetworkServiceEvent::ChannelPendingToBeAccepted(peer_id, channel_id) => { + println!( + "A temp channel ({:?}) to {:?} created", + &channel_id, peer_id + ); + assert_eq!(peer_id, &node_a.peer_id); + Some(channel_id.clone()) + } + _ => None, + }) + .await; + + node_b + .network_actor + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::AcceptChannel( + AcceptChannelCommand { + temp_channel_id: old_channel_id.clone(), + funding_amount: 1000, + }, + None, + ), + )) + .expect("node_a alive"); + + let new_channel_id = node_a + .expect_to_process_event(|event| match event { + NetworkServiceEvent::ChannelCreated(peer_id, channel_id) => { + println!("A channel ({:?}) to {:?} created", &channel_id, &peer_id); + assert_eq!(peer_id, &node_b.peer_id); + Some(channel_id.clone()) + } + _ => None, + }) + .await; + + node_b + .expect_event(|event| match event { + NetworkServiceEvent::ChannelCreated(peer_id, channel_id) => { + println!("A channel ({:?}) to {:?} created", channel_id, peer_id); + assert_eq!(peer_id, &node_a.peer_id); + assert_eq!(channel_id, &new_channel_id); + true + } + _ => false, + }) + .await; + + node_a + .network_actor + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::ControlPcnChannel(ChannelCommandWithId { + channel_id: new_channel_id.clone(), + command: ChannelCommand::CommitmentSigned(), + }), + )) + .expect("node_a alive"); + + println!("node_a send CommitmentSigned to node_b"); + + node_b + .network_actor + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::ControlPcnChannel(ChannelCommandWithId { + channel_id: new_channel_id.clone(), + command: ChannelCommand::CommitmentSigned(), + }), + )) + .expect("node_a alive"); + + println!("node_b send CommitmentSigned to node_a"); + + node_a + .expect_event(|event| match event { + NetworkServiceEvent::ChannelReady(peer_id, channel_id) => { + println!("A channel ({:?}) to {:?} ready", channel_id, peer_id); + assert_eq!(peer_id, &node_b.peer_id); + true + } + _ => false, + }) + .await; + } + #[tokio::test] async fn test_reestablish_channel() { let [mut node_a, mut node_b] = NetworkNode::new_n_interconnected_nodes(2) From f5d662ce3c2fb4cb506d649c012d0499d3733e5d Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 15:50:09 +0800 Subject: [PATCH 04/30] Fix obtaining temp channel id instead of final one --- src/ckb/channel.rs | 18 +++++++++++------- src/ckb/network.rs | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 5f15b818..9efcff16 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -748,6 +748,9 @@ where // See also the notes [state updates across multiple actors](docs/notes/state-update-across-multiple-actors.md). self.network .send_message(NetworkActorMessage::new_event( + // TODO: The channel id here is a temporary channel id, + // while the ChannelCreated event emitted by the counterpart + // is a real channel id. This may cause confusion. NetworkActorEvent::ChannelCreated( channel.get_id(), self.peer_id.clone(), @@ -3679,6 +3682,7 @@ mod tests { #[tokio::test] async fn test_create_channel() { + let _ = env_logger::try_init(); let [mut node_a, mut node_b] = NetworkNode::new_n_interconnected_nodes(2) .await @@ -3724,26 +3728,26 @@ mod tests { )) .expect("node_a alive"); - let new_channel_id = node_a + node_a .expect_to_process_event(|event| match event { NetworkServiceEvent::ChannelCreated(peer_id, channel_id) => { println!("A channel ({:?}) to {:?} created", &channel_id, &peer_id); assert_eq!(peer_id, &node_b.peer_id); + assert_eq!(channel_id, &old_channel_id); Some(channel_id.clone()) } _ => None, }) .await; - node_b - .expect_event(|event| match event { + let new_channel_id = node_b + .expect_to_process_event(|event| match event { NetworkServiceEvent::ChannelCreated(peer_id, channel_id) => { - println!("A channel ({:?}) to {:?} created", channel_id, peer_id); + println!("A channel ({:?}) to {:?} created", &channel_id, &peer_id); assert_eq!(peer_id, &node_a.peer_id); - assert_eq!(channel_id, &new_channel_id); - true + Some(channel_id.clone()) } - _ => false, + _ => None, }) .await; diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 28cf552c..66ad718e 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -987,7 +987,7 @@ where }); debug!("Starting funding channel"); - // TODO: Here we implies the one who receives AcceptChannel message + // TODO: Here we imply the one who receives AcceptChannel message // will send TxUpdate message first. dbg!(&script); myself From ab05f76d7eae85647744fab23e442b3c98a6468b Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 17:32:29 +0800 Subject: [PATCH 05/30] Correctly assemble funding tx --- src/ckb/channel.rs | 9 ++++-- src/ckb_chain/actor.rs | 66 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 9efcff16..75e81796 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -3402,6 +3402,7 @@ mod tests { packed::{CellDep, CellInput, CellOutput, OutPoint, Transaction}, prelude::{AsTransactionBuilder, Pack}, }; + use log::debug; use molecule::prelude::{Builder, Entity}; use crate::{ @@ -3751,6 +3752,10 @@ mod tests { }) .await; + // Wait for each party to create funding txs. + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + debug!("Sending commitment_signed to both parties"); + node_a .network_actor .send_message(NetworkActorMessage::new_command( @@ -3761,7 +3766,7 @@ mod tests { )) .expect("node_a alive"); - println!("node_a send CommitmentSigned to node_b"); + debug!("node_a send CommitmentSigned to node_b"); node_b .network_actor @@ -3773,7 +3778,7 @@ mod tests { )) .expect("node_a alive"); - println!("node_b send CommitmentSigned to node_a"); + debug!("node_b send CommitmentSigned to node_a"); node_a .expect_event(|event| match event { diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index d56e6c4c..15ba0f27 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -220,6 +220,11 @@ pub use test_utils::MockChainActor; mod test_utils { use std::collections::HashMap; + use ckb_types::{ + packed::CellOutput, + prelude::{Builder, Entity, Pack, PackVec, Unpack}, + }; + use super::CkbChainMessage; use crate::ckb::chain::MockContext; @@ -272,12 +277,63 @@ mod test_utils { use CkbChainMessage::*; match message { Fund(tx, request, reply_port) => { - // TODO: Fill in transaction from request. - let fulfilled_tx = tx.clone(); + let mut fulfilled_tx = tx.clone(); + let outputs = fulfilled_tx + .as_ref() + .map(|x| x.outputs()) + .unwrap_or_default(); + let outputs = match outputs.get(0) { + Some(output) => { + if output.lock() != request.script { + error!( + "funding request script ({:?}) does not match the first output lock script ({:?})", request.script, output.lock() + ); + return Ok(()); + } + let current_capacity: u64 = output.capacity().unpack(); + let capacity = request.local_amount + current_capacity; + let mut outputs_builder = outputs.as_builder(); + + outputs_builder + .replace(0, output.as_builder().capacity(capacity.pack()).build()); + outputs_builder.build() + } + None => [CellOutput::new_builder() + .capacity(request.local_amount.pack()) + .lock(request.script.clone()) + .build()] + .pack(), + }; + + let outputs_data = fulfilled_tx + .as_ref() + .map(|x| x.outputs_data()) + .unwrap_or_default(); + let outputs_data = if outputs_data.is_empty() { + [Default::default()].pack() + } else { + outputs_data + }; + + let tx_builder = fulfilled_tx + .take() + .map(|x| x.as_advanced_builder()) + .unwrap_or_default(); + + fulfilled_tx + .update_for_self( + tx_builder + .set_outputs(outputs.into_iter().collect()) + .set_outputs_data(outputs_data.into_iter().collect()) + .build(), + ) + .expect("update tx"); + debug!( - "fulfilling funding request: request: {:?}, original tx: {:?}, fulfilled tx: {:?}", + "Fulfilling funding request: request: {:?}, original tx: {:?}, fulfilled tx: {:?}", request, &tx, &fulfilled_tx ); + if let Err(e) = reply_port.send(Ok(fulfilled_tx)) { error!( "[{}] send reply failed: {:?}", @@ -302,7 +358,7 @@ mod test_utils { } } SendTx(tx) => { - debug!("sending transaction: {:?}", tx); + debug!("Sending transaction: {:?}", tx); // TODO: verify the transaction and set the relevant status. let status = ckb_jsonrpc_types::Status::Committed; debug!("Verified transaction: {:?}, status: {:?}", tx, status); @@ -315,7 +371,7 @@ mod test_utils { .cloned() .unwrap_or(ckb_jsonrpc_types::Status::Unknown); debug!( - "tracing transaction: {:?}, status: {:?}", + "Tracing transaction: {:?}, status: {:?}", &tx.tx_hash, &status ); if let Err(e) = reply_port.send(status) { From 0bb0eb4537e93bcfb0bf0b6961ee8aa9597c2113 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 17:45:11 +0800 Subject: [PATCH 06/30] Add dummy secp256k1 lock in mock environment --- src/ckb/chain.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index f64c21a7..14594190 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -51,6 +51,12 @@ impl MockContext { "../../tests/deploy/contracts/always_success" )), ), + ( + Contract::Secp256k1Lock, + Bytes::from_static(include_bytes!( + "../../tests/deploy/contracts/always_success" + )), + ), ( Contract::CkbAuth, Bytes::from_static(include_bytes!("../../tests/deploy/contracts/auth")), From ef3546d2ad421976cd906d431f715b661bdc32eb Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 17:45:27 +0800 Subject: [PATCH 07/30] Update comments for mock chain actor --- src/ckb_chain/actor.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 15ba0f27..d7c39325 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -343,10 +343,13 @@ mod test_utils { } } Sign(tx, reply_port) => { - // TODO: Fill in transaction from request. + // We don't need to sign the funding transaction in mock chain actor, + // as any funding transaction is considered correct it we can successfully + // run the scripts of transaction inputs, and we don't have inputs in the + // funding transaction. let signed_tx = tx.clone(); debug!( - "signing transaction: original tx: {:?}, signed tx: {:?}", + "Signing transaction: original tx: {:?}, signed tx: {:?}", &tx, &signed_tx ); if let Err(e) = reply_port.send(Ok(signed_tx)) { From 5ad144a621376520b61bda09f3a95a70fa9d5a5c Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 17:47:00 +0800 Subject: [PATCH 08/30] Use mock chain actor instead of hard-coded funding tx generation --- src/ckb/channel.rs | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 75e81796..d3ecfd45 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -2500,41 +2500,6 @@ impl ChannelActorState { format!("Funding transaction output amount mismatch ({} given, {} to local , {} to remote)", first_output_capacity, self.to_local_amount, self.to_remote_amount) )); } - - let commtiment_lock_context = CommitmentLockContext::get(); - // Just create a cell with the same capacity as the first output of the funding tx. - // This is to test the validity of the commitment tx that spends the funding tx. - if commtiment_lock_context.is_testing() { - let always_success = commtiment_lock_context - .get_always_success_script(b"funding transaction test"); - let mut context = commtiment_lock_context.write_mock_context(); - let context = &mut context; - let funding_tx = Transaction::default() - .as_advanced_builder() - .outputs([CellOutput::new_builder() - .capacity(first_output.capacity()) - .lock(always_success) - .build()]) - .outputs_data(vec![Default::default()]) - .build(); - dbg!("funding_tx before completion: {:?}", &funding_tx); - let funding_tx = context.complete_tx(funding_tx); - - dbg!("funding_tx after completion: {:?}", &funding_tx); - - let result = context.verify_tx(&funding_tx, 10_000_000); - dbg!(&result); - assert!(result.is_ok()); - - // Save this transaction so that we can find it later. - let outpoint = funding_tx.output_pts().get(0).unwrap().clone(); - let (cell, cell_data) = funding_tx.output_with_data(0).unwrap(); - dbg!("funding_tx saved: {:?}", &funding_tx); - dbg!("outpoint: {:?}", &outpoint); - dbg!("Cell: {:?}", &cell); - context.create_cell_with_out_point(outpoint, cell, cell_data); - self.funding_tx = Some(funding_tx.data()); - } } } Ok(()) From 93a5a1c64688aab73c584f52ef6f1016e7bd1ee7 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 17:48:27 +0800 Subject: [PATCH 09/30] Remove hard-coded commitment transaction verification --- src/ckb/channel.rs | 118 --------------------------------------------- 1 file changed, 118 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index d3ecfd45..ba5ca1dd 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -2136,124 +2136,6 @@ impl ChannelActorState { let tx = self.build_and_verify_commitment_tx(commitment_signed.partial_signature)?; - let commitment_lock_context = CommitmentLockContext::get(); - // Try to create an transaction which spends the commitment transaction, to - // verify that our code actually works. - if commitment_lock_context.is_testing() { - dbg!("Since we are in testing model, we will now create a revocation transaction to test our construction works"); - let output_lock_script = commitment_lock_context.get_always_success_script(b"whatever"); - - let commitment_tx = self.verify_and_complete_tx(commitment_signed.partial_signature)?; - - println!("The complete commitment transaction is {:?}", commitment_tx); - dbg!( - commitment_tx.hash(), - commitment_tx.cell_deps(), - commitment_tx.inputs(), - commitment_tx.outputs(), - commitment_tx.witnesses() - ); - - let context = commitment_lock_context.read_mock_context(); - let context = &mut context.clone(); - - let revocation_keys = [ - "8172cbf168dcb988d2849ea229603f843614a038e1baa83783aee2f9aeac32ea", - "e16a04c9d5d3d80bc0bb03c19dceddfc34a5017a885a57b9316bd9944022a088", - ] - .iter() - .map(|x| hex::decode(x).unwrap()) - .collect::>(); - dbg!(&revocation_keys); - - // Use the second output as an input to the new transaction. - // The first output is an immediate spendable output, - // while the second output is a delayed output locked by commitment lock. - let commitment_lock_index = 1; - let commitment_out_point = &commitment_tx.output_pts()[commitment_lock_index]; - dbg!("commitment_out_point: {:?}", commitment_out_point); - let commitment_out_point = commitment_tx - .output_pts() - .get(commitment_lock_index) - .unwrap() - .clone(); - let (commitment_lock_cell, commitment_lock_cell_data) = commitment_tx - .output_with_data(commitment_lock_index) - .unwrap(); - dbg!("inputs to new_tx saved: {:?}", &commitment_tx); - dbg!("outpoint: {:?}", &commitment_out_point); - dbg!("Cell: {:?}", &commitment_lock_cell); - context.create_cell_with_out_point( - commitment_out_point.clone(), - commitment_lock_cell, - commitment_lock_cell_data, - ); - // We can verify revocation tx works by - // verify the funding tx, commitment tx and revocation tx verify successfully. - dbg!( - "add the funding tx to test_verify_fixed_tx to verify our construction works", - &self.funding_tx - ); - dbg!( - "add the commitment tx to test_verify_fixed_tx to verify our construction works", - &commitment_tx - ); - - let input = CellInput::new_builder() - .previous_output(commitment_out_point.clone()) - .build(); - - dbg!("input: {:?}", &input); - let revocation_tx = TransactionBuilder::default() - .cell_deps(commitment_tx.cell_deps().clone()) - .inputs(vec![input]) - .outputs(vec![CellOutput::new_builder() - .capacity(20.pack()) - .lock(output_lock_script.clone()) - .build()]) - .outputs_data(vec![Default::default()]) - .build(); - dbg!( - "Built spending transaction with cell deps and inputs: {:?}", - &revocation_tx - ); - let message: [u8; 32] = revocation_tx.hash().as_slice().try_into().unwrap(); - dbg!("message: {:?}", hex::encode(&message)); - let results = revocation_keys - .iter() - .map(|key| { - let signature = ckb_crypto::secp::Privkey::from_slice(&key) - .sign_recoverable(&message.into()) - .unwrap() - .serialize(); - let witness_script = self.build_previous_commitment_transaction_witnesses(false); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); - - let revocation_tx = revocation_tx - .as_advanced_builder() - .witnesses(vec![witness.pack()]) - .build(); - - dbg!("add the revocation tx to test_verify_fixed_tx to verify our construction works", &revocation_tx); - - dbg!( - "Verifying spending transaction of commitment tx: {:?}", - &revocation_tx - ); - - let result = context.verify_tx(&revocation_tx, 10_000_000); - dbg!(&result); - result.is_ok() - }) - .collect::>(); - dbg!("validating results", &results); - // The person who firstly signs the transaction will give the other one - // the right to revoke the commitment transaction. While the other one - // can't revoke the commitment transaction, as this is not signed to - // allow his revocation key to revoke the commitment transaction. - assert!(results == vec![false, true] || results == vec![true, false]); - } - debug!( "Successfully handled commitment signed message: {:?}, tx: {:?}", &commitment_signed, &tx From 3b256047df72ab3aad8a448265ae068fe88a70b1 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 18:13:27 +0800 Subject: [PATCH 10/30] Refine channel ready checking conditions --- src/ckb/channel.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index ba5ca1dd..68376f65 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -3630,8 +3630,27 @@ mod tests { node_a .expect_event(|event| match event { NetworkServiceEvent::ChannelReady(peer_id, channel_id) => { - println!("A channel ({:?}) to {:?} ready", channel_id, peer_id); + println!( + "A channel ({:?}) to {:?} is now ready", + &channel_id, &peer_id + ); assert_eq!(peer_id, &node_b.peer_id); + assert_eq!(channel_id, &new_channel_id); + true + } + _ => false, + }) + .await; + + node_b + .expect_event(|event| match event { + NetworkServiceEvent::ChannelReady(peer_id, channel_id) => { + println!( + "A channel ({:?}) to {:?} is now ready", + &channel_id, &peer_id + ); + assert_eq!(peer_id, &node_a.peer_id); + assert_eq!(channel_id, &new_channel_id); true } _ => false, From cea633ffbb7b4fb2d85049f9e68296408e96a585 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 18:39:56 +0800 Subject: [PATCH 11/30] Emit peer CommitmentSigned event for later use --- src/ckb/channel.rs | 76 ++++++++++++++++++++++++++++++++++++++++------ src/ckb/network.rs | 4 ++- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 68376f65..652f050b 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -34,7 +34,7 @@ use std::{ use crate::{ ckb::{chain::CommitmentLockContext, types::Shutdown}, ckb_chain::FundingRequest, - RpcError, + NetworkServiceEvent, RpcError, }; use super::{ @@ -224,8 +224,11 @@ impl ChannelActor { } }; - let PartiallySignedCommitmentTransaction { tx, signature } = - state.build_and_sign_commitment_tx()?; + let PartiallySignedCommitmentTransaction { + tx, + signature, + num: _, + } = state.build_and_sign_commitment_tx()?; debug!( "Build a funding tx ({:?}) with partial signature {:?}", &tx, &signature @@ -1264,14 +1267,18 @@ impl ChannelActorState { self.remote_channel_parameters.as_ref().unwrap() } - pub fn get_next_commitment_number(&self, local: bool) -> u64 { + pub fn get_current_commitment_number(&self, local: bool) -> u64 { if local { - self.local_commitment_number + 1 + self.local_commitment_number } else { - self.remote_commitment_number + 1 + self.remote_commitment_number } } + pub fn get_next_commitment_number(&self, local: bool) -> u64 { + self.get_current_commitment_number(local) + 1 + } + pub fn get_funding_transaction(&self) -> &Transaction { self.funding_tx .as_ref() @@ -2141,6 +2148,19 @@ impl ChannelActorState { &commitment_signed, &tx ); + // Notify outside observers. + network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::NetworkServiceEvent( + NetworkServiceEvent::RemoteCommitmentSigned( + self.peer_id.clone(), + self.get_id(), + tx.clone(), + ), + ), + )) + .expect("myself alive"); + debug!("Updating peer next local nonce"); self.remote_nonce = Some(commitment_signed.next_local_nonce); match flags { @@ -2704,13 +2724,14 @@ impl ChannelActorState { dbg!("Calling build_commitment_tx from build_and_verify_commitment_tx"); let tx = self.build_commitment_tx(false); + let num = self.get_current_commitment_number(false); let message = get_tx_message_to_sign(&tx); debug!( "Verifying partial signature ({:?}) of commitment tx ({:?}) message {:?}", &signature, &tx, &message ); verify_ctx.verify(signature, message.as_slice())?; - Ok(PartiallySignedCommitmentTransaction { tx, signature }) + Ok(PartiallySignedCommitmentTransaction { tx, signature, num }) } pub fn build_and_sign_commitment_tx( @@ -2719,6 +2740,7 @@ impl ChannelActorState { let sign_ctx = Musig2SignContext::from(self); let tx = self.build_commitment_tx(true); + let num = self.get_current_commitment_number(true); let message = get_tx_message_to_sign(&tx); debug!( @@ -2731,7 +2753,7 @@ impl ChannelActorState { &tx, &message, &signature, ); - Ok(PartiallySignedCommitmentTransaction { tx, signature }) + Ok(PartiallySignedCommitmentTransaction { tx, signature, num }) } /// Verify the partial signature from the peer and create a complete transaction @@ -2787,8 +2809,12 @@ pub trait ChannelActorStateStore { /// the ckb transaction. #[derive(Clone, Debug)] pub struct PartiallySignedCommitmentTransaction { - tx: TransactionView, - signature: PartialSignature, + // The commitment number of the commitment transaction. + pub num: u64, + // The commitment transaction. + pub tx: TransactionView, + // The partial signature of the commitment transaction. + pub signature: PartialSignature, } pub struct Musig2Context { @@ -3627,6 +3653,36 @@ mod tests { debug!("node_b send CommitmentSigned to node_a"); + let _node_b_commitment_tx = node_a + .expect_to_process_event(|event| match event { + NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, tx) => { + println!( + "A commitment tx {:?} from {:?} for channel {:?} received", + &tx, peer_id, channel_id + ); + assert_eq!(peer_id, &node_b.peer_id); + assert_eq!(channel_id, &new_channel_id); + Some(tx.clone()) + } + _ => None, + }) + .await; + + let _node_a_commitment_tx = node_b + .expect_to_process_event(|event| match event { + NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, tx) => { + println!( + "A commitment tx {:?} from {:?} for channel {:?} received", + &tx, peer_id, channel_id + ); + assert_eq!(peer_id, &node_a.peer_id); + assert_eq!(channel_id, &new_channel_id); + Some(tx.clone()) + } + _ => None, + }) + .await; + node_a .expect_event(|event| match event { NetworkServiceEvent::ChannelReady(peer_id, channel_id) => { diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 66ad718e..a1b233a9 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -35,7 +35,7 @@ use tokio_util::task::TaskTracker; use super::channel::{ ChannelActorMessage, ChannelActorStateStore, ChannelCommandWithId, ChannelEvent, - ProcessingChannelError, ProcessingChannelResult, + PartiallySignedCommitmentTransaction, ProcessingChannelError, ProcessingChannelResult, }; use super::key::blake2b_hash_with_salt; use super::types::{Hash256, OpenChannel}; @@ -144,6 +144,8 @@ pub enum NetworkServiceEvent { ChannelReady(PeerId, Hash256), ChannelShutDown(PeerId, Hash256), ChannelClosed(PeerId, Hash256, TransactionView), + // The other party has signed a valid commitment transaction. + RemoteCommitmentSigned(PeerId, Hash256, PartiallySignedCommitmentTransaction), } /// Events that can be sent to the network actor. Except for NetworkServiceEvent, From 2ee4632f29a31940577a269c470cf8c08bd40105 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 19:01:40 +0800 Subject: [PATCH 12/30] Emit CommitmentSignaturePending event --- src/ckb/channel.rs | 102 +++++++++++++++++++++++++++++++++++---------- src/ckb/network.rs | 2 + 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 652f050b..02f5583f 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -50,6 +50,11 @@ use super::{ }; const FUNDING_CELL_WITNESS_LEN: usize = 8 + 36 + 32 + 64; +// Some part of the code liberally gets previous commitment number, which is +// the current commitment number minus 1. We deliberately set initial commitment number to 1, +// so that we can get previous commitment point/number without checking if the channel +// is funded or not. +pub const INITIAL_COMMITMENT_NUMBER: u64 = 1; pub enum ChannelActorMessage { /// Command are the messages that are sent to the channel actor to perform some action. @@ -1124,21 +1129,17 @@ impl ChannelActorState { remote_commitment_point: Pubkey, remote_prev_commitment_point: Pubkey, ) -> Self { - let commitment_number = 1; let signer = InMemorySigner::generate_from_seed(seed); - let local_pubkeys = signer.to_channel_public_keys(commitment_number); + let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); let channel_id = derive_channel_id_from_revocation_keys( &local_pubkeys.revocation_base_key, &remote_pubkeys.revocation_base_key, ); - let remote_commitment_number = 1; - debug!( - "Generated channel id ({:?}) for temporary channel {:?} with local commitment number {:?} and remote commitment number {:?}", + "Generated channel id ({:?}) for temporary channel {:?}", &channel_id, &temp_channel_id, - commitment_number, remote_commitment_number ); Self { @@ -1163,8 +1164,8 @@ impl ChannelActorState { pubkeys: remote_pubkeys, selected_contest_delay: remote_delay, }), - local_commitment_number: commitment_number, - remote_commitment_number, + local_commitment_number: INITIAL_COMMITMENT_NUMBER, + remote_commitment_number: INITIAL_COMMITMENT_NUMBER, remote_shutdown_script: None, remote_nonce: Some(remote_nonce), remote_commitment_points: vec![remote_prev_commitment_point, remote_commitment_point], @@ -1182,8 +1183,7 @@ impl ChannelActorState { to_local_delay: LockTime, ) -> Self { let signer = InMemorySigner::generate_from_seed(seed); - let commitment_number = 1; - let local_pubkeys = signer.to_channel_public_keys(commitment_number); + let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); let temp_channel_id = derive_temp_channel_id_from_revocation_key(&local_pubkeys.revocation_base_key); Self { @@ -1204,9 +1204,9 @@ impl ChannelActorState { selected_contest_delay: to_local_delay, }, remote_channel_parameters: None, - local_commitment_number: commitment_number, + local_commitment_number: INITIAL_COMMITMENT_NUMBER, remote_nonce: None, - remote_commitment_number: 1, + remote_commitment_number: INITIAL_COMMITMENT_NUMBER, remote_commitment_points: vec![], local_shutdown_script: None, local_shutdown_fee: None, @@ -1560,6 +1560,18 @@ impl ChannelActorState { if !flags.contains(SigningCommitmentFlags::OUR_COMMITMENT_SIGNED_SENT) { // TODO: maybe we should send our commitment_signed message here. debug!("CommitmentSigned message received, but we haven't sent our commitment_signed message yet"); + // Notify outside observers. + network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::NetworkServiceEvent( + NetworkServiceEvent::CommitmentSignaturePending( + self.peer_id.clone(), + self.get_id(), + self.get_current_commitment_number(false), + ), + ), + )) + .expect("myself alive"); } } Ok(()) @@ -2071,9 +2083,22 @@ impl ChannelActorState { } TxCollaborationMsg::TxComplete(_msg) => { self.check_tx_complete_preconditions()?; - self.update_state(ChannelState::CollaboratingFundingTx( - flags | CollaboratingFundingTxFlags::THEIR_TX_COMPLETE_SENT, - )); + let flags = flags | CollaboratingFundingTxFlags::THEIR_TX_COMPLETE_SENT; + self.update_state(ChannelState::CollaboratingFundingTx(flags)); + if flags.contains(CollaboratingFundingTxFlags::COLLABRATION_COMPLETED) { + // Notify outside observers. + network + .send_message(NetworkActorMessage::new_event( + NetworkActorEvent::NetworkServiceEvent( + NetworkServiceEvent::CommitmentSignaturePending( + self.peer_id.clone(), + self.get_id(), + self.get_current_commitment_number(false), + ), + ), + )) + .expect("myself alive"); + } } } Ok(()) @@ -3275,12 +3300,11 @@ mod tests { packed::{CellDep, CellInput, CellOutput, OutPoint, Transaction}, prelude::{AsTransactionBuilder, Pack}, }; - use log::debug; use molecule::prelude::{Builder, Entity}; use crate::{ ckb::{ - channel::{ChannelCommand, ChannelCommandWithId}, + channel::{ChannelCommand, ChannelCommandWithId, INITIAL_COMMITMENT_NUMBER}, network::{AcceptChannelCommand, OpenChannelCommand}, test_utils::NetworkNode, NetworkActorCommand, NetworkActorMessage, @@ -3625,9 +3649,25 @@ mod tests { }) .await; - // Wait for each party to create funding txs. - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - debug!("Sending commitment_signed to both parties"); + node_a + .expect_event(|event| match event { + NetworkServiceEvent::CommitmentSignaturePending( + peer_id, + channel_id, + commitment_num, + ) => { + println!( + "A commitment signature for channel {:?} to {:?} is pending", + &channel_id, &peer_id + ); + assert_eq!(peer_id, &node_b.peer_id); + assert_eq!(channel_id, &new_channel_id); + assert_eq!(commitment_num, &INITIAL_COMMITMENT_NUMBER); + true + } + _ => false, + }) + .await; node_a .network_actor @@ -3639,7 +3679,25 @@ mod tests { )) .expect("node_a alive"); - debug!("node_a send CommitmentSigned to node_b"); + node_b + .expect_event(|event| match event { + NetworkServiceEvent::CommitmentSignaturePending( + peer_id, + channel_id, + commitment_num, + ) => { + println!( + "A commitment signature for channel {:?} to {:?} is pending", + &channel_id, &peer_id + ); + assert_eq!(peer_id, &node_a.peer_id); + assert_eq!(channel_id, &new_channel_id); + assert_eq!(commitment_num, &INITIAL_COMMITMENT_NUMBER); + true + } + _ => false, + }) + .await; node_b .network_actor @@ -3651,8 +3709,6 @@ mod tests { )) .expect("node_a alive"); - debug!("node_b send CommitmentSigned to node_a"); - let _node_b_commitment_tx = node_a .expect_to_process_event(|event| match event { NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, tx) => { diff --git a/src/ckb/network.rs b/src/ckb/network.rs index a1b233a9..45e6f3ad 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -144,6 +144,8 @@ pub enum NetworkServiceEvent { ChannelReady(PeerId, Hash256), ChannelShutDown(PeerId, Hash256), ChannelClosed(PeerId, Hash256, TransactionView), + // A CommitmentSigned message is pending to sent to the other party. + CommitmentSignaturePending(PeerId, Hash256, u64), // The other party has signed a valid commitment transaction. RemoteCommitmentSigned(PeerId, Hash256, PartiallySignedCommitmentTransaction), } From 14b4ddf26d0a1bfdec04285d448bdca7b0451e46 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 19:48:48 +0800 Subject: [PATCH 13/30] Correctly save tx from SendTx to mock context --- src/ckb/chain.rs | 8 ++++++++ src/ckb_chain/actor.rs | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index 14594190..c1fb6af0 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -153,6 +153,14 @@ impl MockContext { INSTANCE.get_or_init(|| Self::new()); INSTANCE.get().unwrap() } + + pub fn write(&self) -> RwLockWriteGuard { + self.context.write().unwrap() + } + + pub fn read(&self) -> RwLockReadGuard { + self.context.read().unwrap() + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index d7c39325..8a71cb56 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -361,10 +361,27 @@ mod test_utils { } } SendTx(tx) => { - debug!("Sending transaction: {:?}", tx); - // TODO: verify the transaction and set the relevant status. - let status = ckb_jsonrpc_types::Status::Committed; - debug!("Verified transaction: {:?}, status: {:?}", tx, status); + const MAX_CYCLES: u64 = 100_000_000; + let mut context = ctx.ctx.write(); + let status = match context.verify_tx(&tx, MAX_CYCLES) { + Ok(c) => { + debug!("Verified transaction: {:?} with {} CPU cycles", tx, c); + // Also save the outputs to the context, so that we can refer to + // these out points later. + for outpoint in tx.output_pts().into_iter() { + let index: u32 = outpoint.index().unpack(); + let index = index as usize; + let cell = tx.outputs().get(index).unwrap(); + let data = tx.outputs_data().get(index).unwrap(); + context.create_cell_with_out_point(outpoint, cell, data.as_bytes()); + } + ckb_jsonrpc_types::Status::Committed + } + Err(e) => { + error!("Failed to verify transaction: {:?}, error: {:?}", tx, e); + ckb_jsonrpc_types::Status::Rejected + } + }; ctx.committed_tx_status.insert(tx.hash(), status); } TraceTx(tx, reply_port) => { From 6c1a56661e977502f7b8d72c42e9e32cb8ee187a Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 20:31:43 +0800 Subject: [PATCH 14/30] Verify that we can spend the commitment transaction --- src/ckb/chain.rs | 1 + src/ckb/channel.rs | 30 ++++++++++++++++++++--------- src/ckb/network.rs | 8 +++++--- src/ckb/test_utils.rs | 43 ++++++++++++++++++++++++++++++------------ src/ckb_chain/actor.rs | 18 +++++++++++++----- 5 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index c1fb6af0..3b24f8f3 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -144,6 +144,7 @@ impl MockContext { cell_deps: cell_dep_vec, }), }; + debug!("Created mock context to test transactions."); context } diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 02f5583f..5b2126de 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -2166,7 +2166,8 @@ impl ChannelActorState { } }; - let tx = self.build_and_verify_commitment_tx(commitment_signed.partial_signature)?; + let tx = self.verify_and_complete_tx(commitment_signed.partial_signature)?; + let num = self.get_current_commitment_number(false); debug!( "Successfully handled commitment signed message: {:?}, tx: {:?}", @@ -2180,6 +2181,7 @@ impl ChannelActorState { NetworkServiceEvent::RemoteCommitmentSigned( self.peer_id.clone(), self.get_id(), + num, tx.clone(), ), ), @@ -3294,6 +3296,7 @@ impl InMemorySigner { #[cfg(test)] mod tests { + use ckb_jsonrpc_types::Status; use ckb_sdk::core::TransactionBuilder; use ckb_types::{ core::{DepType, ScriptHashType}, @@ -3709,12 +3712,12 @@ mod tests { )) .expect("node_a alive"); - let _node_b_commitment_tx = node_a + let node_a_commitment_tx = node_a .expect_to_process_event(|event| match event { - NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, tx) => { + NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, num, tx) => { println!( - "A commitment tx {:?} from {:?} for channel {:?} received", - &tx, peer_id, channel_id + "Commitment tx (#{}) {:?} from {:?} for channel {:?} received", + num, &tx, peer_id, channel_id ); assert_eq!(peer_id, &node_b.peer_id); assert_eq!(channel_id, &new_channel_id); @@ -3724,12 +3727,12 @@ mod tests { }) .await; - let _node_a_commitment_tx = node_b + let node_b_commitment_tx = node_b .expect_to_process_event(|event| match event { - NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, tx) => { + NetworkServiceEvent::RemoteCommitmentSigned(peer_id, channel_id, num, tx) => { println!( - "A commitment tx {:?} from {:?} for channel {:?} received", - &tx, peer_id, channel_id + "Commitment tx (#{}) {:?} from {:?} for channel {:?} received", + num, &tx, peer_id, channel_id ); assert_eq!(peer_id, &node_a.peer_id); assert_eq!(channel_id, &new_channel_id); @@ -3768,6 +3771,15 @@ mod tests { _ => false, }) .await; + + assert_eq!( + node_a.submit_tx(node_a_commitment_tx.clone()).await, + Status::Committed + ); + assert_eq!( + node_b.submit_tx(node_b_commitment_tx.clone()).await, + Status::Committed + ); } #[tokio::test] diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 45e6f3ad..bd5373f2 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -35,7 +35,7 @@ use tokio_util::task::TaskTracker; use super::channel::{ ChannelActorMessage, ChannelActorStateStore, ChannelCommandWithId, ChannelEvent, - PartiallySignedCommitmentTransaction, ProcessingChannelError, ProcessingChannelResult, + ProcessingChannelError, ProcessingChannelResult, }; use super::key::blake2b_hash_with_salt; use super::types::{Hash256, OpenChannel}; @@ -146,8 +146,10 @@ pub enum NetworkServiceEvent { ChannelClosed(PeerId, Hash256, TransactionView), // A CommitmentSigned message is pending to sent to the other party. CommitmentSignaturePending(PeerId, Hash256, u64), - // The other party has signed a valid commitment transaction. - RemoteCommitmentSigned(PeerId, Hash256, PartiallySignedCommitmentTransaction), + // The other party has signed a valid commitment transaction, + // and we successfully assemble the partial signature from other party + // to create a complete commitment transaction. + RemoteCommitmentSigned(PeerId, Hash256, u64, TransactionView), } /// Events that can be sent to the network actor. Except for NetworkServiceEvent, diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index 38f8ea22..1a1f566e 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -8,7 +8,9 @@ use std::{ time::Duration, }; -use ractor::{Actor, ActorRef}; +use ckb_types::core::TransactionView; + +use ractor::{call_t, Actor, ActorRef}; use tempfile::TempDir as OldTempDir; use tentacle::{multiaddr::MultiAddr, secio::PeerId}; @@ -21,7 +23,7 @@ use tokio::{ use crate::{ actors::{RootActor, RootActorMessage}, ckb::{chain::CommitmentLockContext, config::CkbNetwork}, - ckb_chain::MockChainActor, + ckb_chain::{CkbChainMessage, MockChainActor, TraceTxRequest}, tasks::{new_tokio_cancellation_token, new_tokio_task_tracker}, CkbConfig, NetworkServiceEvent, }; @@ -90,6 +92,7 @@ pub struct NetworkNode { pub base_dir: TempDir, pub listening_addr: MultiAddr, pub network_actor: ActorRef, + pub chain_actor: ActorRef, pub peer_id: PeerId, pub event_emitter: mpsc::Receiver, } @@ -109,19 +112,14 @@ impl NetworkNode { let mut chain_base_dir = PathBuf::from(base_dir.as_ref()); chain_base_dir.push("ckb-chain"); - let mock_chain_actor = - Actor::spawn_linked(None, MockChainActor::new(), (), root.get_cell()) - .await - .expect("start mock chain actor") - .0; + let chain_actor = Actor::spawn_linked(None, MockChainActor::new(), (), root.get_cell()) + .await + .expect("start mock chain actor") + .0; let network_actor = Actor::spawn_linked( Some(format!("network actor at {:?}", base_dir.as_ref())), - NetworkActor::new( - event_sender, - mock_chain_actor.clone(), - MemoryStore::default(), - ), + NetworkActor::new(event_sender, chain_actor.clone(), MemoryStore::default()), (ckb_config, new_tokio_task_tracker()), root.get_cell(), ) @@ -150,6 +148,7 @@ impl NetworkNode { base_dir, listening_addr, network_actor, + chain_actor, peer_id, event_emitter: event_receiver, } @@ -217,6 +216,26 @@ impl NetworkNode { self.expect_to_process_event(|event| if event_filter(event) { Some(()) } else { None }) .await; } + + pub async fn submit_tx(&mut self, tx: TransactionView) -> ckb_jsonrpc_types::Status { + pub const TIMEOUT: u64 = 1000; + let tx_hash = tx.hash(); + + self.chain_actor + .send_message(CkbChainMessage::SendTx(tx)) + .expect("chain actor alive"); + let request = TraceTxRequest { + tx_hash, + confirmations: 1, + }; + call_t!( + self.chain_actor, + CkbChainMessage::TraceTx, + TIMEOUT, + request.clone() + ) + .expect("chain actor alive") + } } #[derive(Clone, Default)] diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 8a71cb56..4ec74a50 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -272,7 +272,7 @@ mod test_utils { &self, myself: ActorRef, message: Self::Msg, - ctx: &mut Self::State, + state: &mut Self::State, ) -> Result<(), ActorProcessingErr> { use CkbChainMessage::*; match message { @@ -362,7 +362,7 @@ mod test_utils { } SendTx(tx) => { const MAX_CYCLES: u64 = 100_000_000; - let mut context = ctx.ctx.write(); + let mut context = state.ctx.write(); let status = match context.verify_tx(&tx, MAX_CYCLES) { Ok(c) => { debug!("Verified transaction: {:?} with {} CPU cycles", tx, c); @@ -373,7 +373,15 @@ mod test_utils { let index = index as usize; let cell = tx.outputs().get(index).unwrap(); let data = tx.outputs_data().get(index).unwrap(); - context.create_cell_with_out_point(outpoint, cell, data.as_bytes()); + debug!( + "Creating cell with outpoint: {:?}, cell: {:?}, data: {:?}", + outpoint, cell, data + ); + context.create_cell_with_out_point( + outpoint.clone(), + cell, + data.as_bytes(), + ); } ckb_jsonrpc_types::Status::Committed } @@ -382,10 +390,10 @@ mod test_utils { ckb_jsonrpc_types::Status::Rejected } }; - ctx.committed_tx_status.insert(tx.hash(), status); + state.committed_tx_status.insert(tx.hash(), status); } TraceTx(tx, reply_port) => { - let status = ctx + let status = state .committed_tx_status .get(&tx.tx_hash) .cloned() From 2bf084f53a282fb559a1753464c911aabc5c40dc Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 20:40:38 +0800 Subject: [PATCH 15/30] Put testing helper MockContext behind cfg(test) --- src/ckb/chain.rs | 119 ++++++++++------------------------------------- 1 file changed, 25 insertions(+), 94 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index 3b24f8f3..36abfc74 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -1,38 +1,34 @@ -use ckb_testtool::{ckb_types::bytes::Bytes, context::Context}; use ckb_types::{ core::{DepType, ScriptHashType}, packed::{CellDep, CellDepVec, OutPoint, Script}, - prelude::{Builder, Entity, PackVec}, + prelude::{Builder, Entity, Pack, PackVec}, }; use log::debug; use once_cell::sync::OnceCell; -use std::{ - collections::HashMap, - env, - str::FromStr, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; - -use ckb_types::prelude::Pack; +use std::{collections::HashMap, env, str::FromStr, sync::Arc}; use super::{config::CkbNetwork, types::Hash256}; -// TODO: MockContext should only be used in tests. -// We previously used MockContext with CommitmentLockContext::is_testing -// to determine if we are in a testing environment. If we are in a testing environment, -// we try to validate transactions with ckb_testtool. -// We should eventually remove this and write unit tests to validate transactions. -#[derive(Debug)] +#[cfg(not(test))] +use ckb_types::bytes::Bytes; + +#[cfg(test)] +use ckb_testtool::{ckb_types::bytes::Bytes, context::Context}; +#[cfg(test)] +use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + +#[cfg(test)] +#[derive(Clone, Debug)] pub struct MockContext { - context: RwLock, + context: Arc>, contracts_context: Arc, } +#[cfg(test)] impl MockContext { // If we are using cfg(test), then directly including contracts binaries into the // resulting executable is not a problem. Otherwise, we'd better read the binaries from // the filesystem. - #[cfg(test)] fn get_contract_binaries() -> Vec<(Contract, Bytes)> { [ ( @@ -69,47 +65,6 @@ impl MockContext { .into() } - #[cfg(not(test))] - fn get_contract_binaries() -> Vec<(Contract, Bytes)> { - use log::warn; - use std::{fs, path::PathBuf}; - - match env::var("TESTING_CONTRACTS_DIR") { - Ok(base_dir) => { - [ - (Contract::FundingLock, "funding-lock"), - (Contract::CommitmentLock, "commitment-lock"), - (Contract::AlwaysSuccess, "always_success"), - // These are contracts that we will call from other contracts, e.g. funding-lock. - (Contract::CkbAuth, "auth"), - (Contract::SimpleUDT, "simple_udt"), - ] - .into_iter() - .map(|(contract, binary_name)| { - let mut path = PathBuf::from(base_dir.clone()); - path.push(PathBuf::from(binary_name)); - - let binary = fs::read(&path).expect( - format!( - "Failed to read contract binary from path: {:?}", - path.as_path() - ) - .as_str(), - ); - (contract, binary.into()) - }) - .collect() - } - Err(e) => { - warn!( - "TESTING_CONTRACTS_DIR is not set, using default contracts: {:?}", - e - ); - vec![] - } - } - } - pub fn new() -> Self { let mut context = Context::default(); @@ -138,7 +93,7 @@ impl MockContext { ); let context = MockContext { - context: RwLock::new(context), + context: Arc::new(RwLock::new(context)), contracts_context: Arc::new(ContractsContext { contract_default_scripts: map, cell_deps: cell_dep_vec, @@ -148,13 +103,6 @@ impl MockContext { context } - // This is used temporarily to test the functionality of the contract. - pub fn get() -> &'static Self { - static INSTANCE: OnceCell = OnceCell::new(); - INSTANCE.get_or_init(|| Self::new()); - INSTANCE.get().unwrap() - } - pub fn write(&self) -> RwLockWriteGuard { self.context.write().unwrap() } @@ -171,6 +119,7 @@ enum Contract { Secp256k1Lock, AlwaysSuccess, CkbAuth, + #[allow(dead_code)] SimpleUDT, } @@ -183,7 +132,8 @@ pub struct ContractsContext { #[derive(Clone, Debug)] pub enum CommitmentLockContext { - Mock(&'static MockContext), + #[cfg(test)] + Mock(MockContext), Real(Arc), } @@ -254,7 +204,11 @@ impl CommitmentLockContext { // because it is used in so many places. pub fn initialize(network: CkbNetwork) -> &'static Self { COMMITMENT_LOCK_CTX_INSTANCE.get_or_init(|| match network { - CkbNetwork::Mocknet => Self::Mock(MockContext::get()), + #[cfg(test)] + CkbNetwork::Mocknet => { + log::warn!("Initializing mock context for testing."); + Self::Mock(MockContext::new()) + } CkbNetwork::Dev => { let mut map = HashMap::new(); let mut cell_deps = vec![]; @@ -415,29 +369,20 @@ impl CommitmentLockContext { COMMITMENT_LOCK_CTX_INSTANCE.get().unwrap() } - pub fn get_mock() -> Self { - Self::Mock(MockContext::get()) - } - pub fn get() -> &'static Self { COMMITMENT_LOCK_CTX_INSTANCE.get().unwrap() } - pub fn is_testing(&self) -> bool { - match self { - Self::Mock(_) => true, - Self::Real(_) => false, - } - } - fn get_contracts_map(&self) -> &HashMap { match self { + #[cfg(test)] Self::Mock(mock) => &mock.contracts_context.contract_default_scripts, Self::Real(real) => &real.contract_default_scripts, } } fn get_cell_deps(&self) -> &CellDepVec { match self { + #[cfg(test)] Self::Mock(mock) => &mock.contracts_context.cell_deps, Self::Real(real) => &real.cell_deps, } @@ -453,20 +398,6 @@ impl CommitmentLockContext { .build() } - pub fn read_mock_context(&self) -> RwLockReadGuard { - match &self { - Self::Mock(mock) => mock.context.read().unwrap(), - Self::Real(_real) => panic!("Real context is not readable"), - } - } - - pub fn write_mock_context(&self) -> RwLockWriteGuard { - match &self { - Self::Mock(mock) => mock.context.write().unwrap(), - Self::Real(_real) => panic!("Real context is not writable"), - } - } - pub fn get_secp256k1_lock_script(&self, args: &[u8]) -> Script { self.get_script(Contract::Secp256k1Lock, args) } From a463b418c9894dab50c06aef163a6fad8096a193 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 21:21:23 +0800 Subject: [PATCH 16/30] Remove singleton for get contract info --- src/ckb/chain.rs | 17 +++++------------ src/ckb/channel.rs | 27 +++++++++++++++++++++------ src/ckb/network.rs | 35 +++++++++++++++++++++++++++-------- src/ckb/test_utils.rs | 9 +++++++-- src/main.rs | 3 ++- 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index 36abfc74..a2a27465 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -4,7 +4,7 @@ use ckb_types::{ prelude::{Builder, Entity, Pack, PackVec}, }; use log::debug; -use once_cell::sync::OnceCell; + use std::{collections::HashMap, env, str::FromStr, sync::Arc}; use super::{config::CkbNetwork, types::Hash256}; @@ -123,7 +123,7 @@ enum Contract { SimpleUDT, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ContractsContext { contract_default_scripts: HashMap, // TODO: We bundle all the cell deps together, but some of they are not always needed. @@ -197,13 +197,11 @@ fn get_environment_variable( ) } -static COMMITMENT_LOCK_CTX_INSTANCE: OnceCell = OnceCell::new(); - impl CommitmentLockContext { // TODO: better way to organize this? Currently CommitmentLockContext is a singleton // because it is used in so many places. - pub fn initialize(network: CkbNetwork) -> &'static Self { - COMMITMENT_LOCK_CTX_INSTANCE.get_or_init(|| match network { + pub fn new(network: CkbNetwork) -> Self { + match network { #[cfg(test)] CkbNetwork::Mocknet => { log::warn!("Initializing mock context for testing."); @@ -365,12 +363,7 @@ impl CommitmentLockContext { })) } _ => panic!("Unsupported network type {:?}", network), - }); - COMMITMENT_LOCK_CTX_INSTANCE.get().unwrap() - } - - pub fn get() -> &'static Self { - COMMITMENT_LOCK_CTX_INSTANCE.get().unwrap() + } } fn get_contracts_map(&self) -> &HashMap { diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 5b2126de..0c93bf39 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -562,12 +562,12 @@ where { type Msg = ChannelActorMessage; type State = ChannelActorState; - type Arguments = ChannelInitializationParameter; + type Arguments = (CommitmentLockContext, ChannelInitializationParameter); async fn pre_start( &self, myself: ActorRef, - args: Self::Arguments, + (ctx, args): Self::Arguments, ) -> Result { // startup the event processing match args { @@ -622,6 +622,7 @@ where next_local_nonce.clone(), *first_per_commitment_point, *second_per_commitment_point, + ctx, ); let commitment_number = 0; @@ -684,6 +685,7 @@ where self.peer_id.clone(), funding_amount, LockTime::new(DEFAULT_TO_LOCAL_DELAY_BLOCKS), + ctx, ); let commitment_number = 0; @@ -938,6 +940,11 @@ pub struct ChannelActorState { pub local_shutdown_fee: Option, pub remote_shutdown_signature: Option, pub remote_shutdown_fee: Option, + + // Providing additional information about contracts (e.g. commitmemt lock outpoint) + // that are used in pcn channels. + #[serde(skip)] + pub contracts_context: Option, } #[derive(PartialEq, Eq, Clone, Debug)] @@ -1128,6 +1135,7 @@ impl ChannelActorState { remote_nonce: PubNonce, remote_commitment_point: Pubkey, remote_prev_commitment_point: Pubkey, + contracts_context: CommitmentLockContext, ) -> Self { let signer = InMemorySigner::generate_from_seed(seed); let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); @@ -1173,6 +1181,7 @@ impl ChannelActorState { local_shutdown_fee: None, remote_shutdown_signature: None, remote_shutdown_fee: None, + contracts_context: Some(contracts_context), } } @@ -1181,6 +1190,7 @@ impl ChannelActorState { peer_id: PeerId, value: u128, to_local_delay: LockTime, + contracts_context: CommitmentLockContext, ) -> Self { let signer = InMemorySigner::generate_from_seed(seed); let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); @@ -1214,6 +1224,7 @@ impl ChannelActorState { remote_shutdown_fee: None, local_shutdown_signature: None, remote_shutdown_signature: None, + contracts_context: Some(contracts_context), } } @@ -1232,6 +1243,10 @@ impl ChannelActorState { self.id } + pub fn get_contracts_context(&self) -> &CommitmentLockContext { + self.contracts_context.as_ref().unwrap() + } + pub fn get_local_nonce(&self) -> impl Borrow { self.get_next_local_secnonce().public_nonce() } @@ -1349,7 +1364,7 @@ impl ChannelActorState { } pub fn get_funding_lock_script(&self) -> Script { - let ctx: &CommitmentLockContext = CommitmentLockContext::get(); + let ctx: &CommitmentLockContext = self.get_contracts_context(); let args = blake2b_256(self.get_funding_lock_script_xonly()); debug!( "Aggregated pubkey: {:?}, hash: {:?}", @@ -2520,7 +2535,7 @@ impl ChannelActorState { ))); } }; - let commitment_lock_context = CommitmentLockContext::get(); + let commitment_lock_context = self.get_contracts_context(); let tx_builder = TransactionBuilder::default() .cell_deps(commitment_lock_context.get_commitment_transaction_cell_deps()) .input( @@ -2557,7 +2572,7 @@ impl ChannelActorState { // for the remote party (we build this commitment transaction // normally because we want to send a partial signature to remote). pub fn build_commitment_tx(&self, local: bool) -> TransactionView { - let commitment_lock_context = CommitmentLockContext::get(); + let commitment_lock_context = self.get_contracts_context(); let input = self.get_funding_transaction_outpoint(); let tx_builder = TransactionBuilder::default() .cell_deps(commitment_lock_context.get_commitment_transaction_cell_deps()) @@ -2722,7 +2737,7 @@ impl ChannelActorState { self.remote_commitment_number ); - let commitment_lock_ctx = CommitmentLockContext::get(); + let commitment_lock_ctx = self.get_contracts_context(); let immediate_secp256k1_lock_script = commitment_lock_ctx .get_secp256k1_lock_script(&blake2b_256(immediate_payment_key.serialize())[0..20]); let commitment_lock_script = diff --git a/src/ckb/network.rs b/src/ckb/network.rs index bd5373f2..4b25167e 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -33,6 +33,7 @@ use tentacle::{ use tokio::sync::{mpsc, oneshot}; use tokio_util::task::TaskTracker; +use super::chain::CommitmentLockContext; use super::channel::{ ChannelActorMessage, ChannelActorStateStore, ChannelCommandWithId, ChannelEvent, ProcessingChannelError, ProcessingChannelResult, @@ -229,6 +230,8 @@ pub struct NetworkActor { // An event emitter to notify ourside observers. event_sender: mpsc::Sender, chain_actor: ActorRef, + // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. + ctx: CommitmentLockContext, store: S, } @@ -239,11 +242,14 @@ where pub fn new( event_sender: mpsc::Sender, chain_actor: ActorRef, + // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. + ctx: CommitmentLockContext, store: S, ) -> Self { Self { event_sender, chain_actor, + ctx, store, } } @@ -535,6 +541,7 @@ pub struct NetworkActorState { pending_channels: HashMap, // Used to broadcast and query network info. chain_actor: ActorRef, + contracts_context: CommitmentLockContext, } impl NetworkActorState { @@ -565,7 +572,10 @@ impl NetworkActorState { let channel = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), network.clone(), store), - ChannelInitializationParameter::OpenChannel(funding_amount, seed, tx), + ( + self.contracts_context.clone(), + ChannelInitializationParameter::OpenChannel(funding_amount, seed, tx), + ), network.clone().get_cell(), ) .await? @@ -602,11 +612,14 @@ impl NetworkActorState { let channel = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), network.clone(), store), - ChannelInitializationParameter::AcceptChannel( - funding_amount, - seed, - open_channel, - Some(tx), + ( + self.contracts_context.clone(), + ChannelInitializationParameter::AcceptChannel( + funding_amount, + seed, + open_channel, + Some(tx), + ), ), network.clone().get_cell(), ) @@ -670,7 +683,10 @@ impl NetworkActorState { if let Ok((channel, _)) = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), self.network.clone(), store.clone()), - ChannelInitializationParameter::ReestablishChannel(channel_id), + ( + self.contracts_context.clone(), + ChannelInitializationParameter::ReestablishChannel(channel_id), + ), self.network.get_cell(), ) .await @@ -930,6 +946,7 @@ where to_be_accepted_channels: Default::default(), pending_channels: Default::default(), chain_actor: self.chain_actor.clone(), + contracts_context: self.ctx.clone(), }) } @@ -1234,11 +1251,13 @@ pub async fn start_ckb, tracker: TaskTracker, root_actor: ActorCell, + // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. + ctx: CommitmentLockContext, store: S, ) -> ActorRef { let (actor, _handle) = Actor::spawn_linked( Some("network actor".to_string()), - NetworkActor::new(event_sender, chain_actor, store), + NetworkActor::new(event_sender, chain_actor, ctx, store), (config, tracker), root_actor, ) diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index 1a1f566e..f7f3e194 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -105,7 +105,7 @@ impl NetworkNode { ..Default::default() }; - CommitmentLockContext::initialize(CkbNetwork::Mocknet); + let ctx = CommitmentLockContext::new(CkbNetwork::Mocknet); let root = ROOT_ACTOR.get_or_init(get_test_root_actor).await.clone(); let (event_sender, mut event_receiver) = mpsc::channel(10000); @@ -119,7 +119,12 @@ impl NetworkNode { let network_actor = Actor::spawn_linked( Some(format!("network actor at {:?}", base_dir.as_ref())), - NetworkActor::new(event_sender, chain_actor.clone(), MemoryStore::default()), + NetworkActor::new( + event_sender, + chain_actor.clone(), + ctx, + MemoryStore::default(), + ), (ckb_config, new_tokio_task_tracker()), root.get_cell(), ) diff --git a/src/main.rs b/src/main.rs index 49c734bc..5768f34e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,7 @@ pub async fn main() { let ckb_chain_config = config.ckb_chain.expect("ckb-chain service is required for ckb service. Add ckb-chain service to the services list in the config file and relevant configuration to the ckb_chain section of the config file."); let network = ckb_config.network.unwrap_or(CkbNetwork::Dev); - let ctx = CommitmentLockContext::initialize(network); + let ctx = CommitmentLockContext::new(network); let ckb_chain_actor = Actor::spawn_linked( Some("ckb-chain".to_string()), @@ -77,6 +77,7 @@ pub async fn main() { event_sender, new_tokio_task_tracker(), root_actor.get_cell(), + ctx, store.clone(), ) .await; From fe9aba7afac2fa6e93200965ebc8cae9a45443fe Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 21:30:08 +0800 Subject: [PATCH 17/30] Make CommtimentLockContext less verbose --- src/ckb/chain.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index a2a27465..ec6af926 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -130,13 +130,23 @@ pub struct ContractsContext { cell_deps: CellDepVec, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum CommitmentLockContext { #[cfg(test)] Mock(MockContext), Real(Arc), } +impl std::fmt::Debug for CommitmentLockContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + #[cfg(test)] + Self::Mock(_mock) => write!(f, "Mock"), + Self::Real(_real) => write!(f, "Real"), + } + } +} + enum EnvironmentVariableType { CodeHash, // FIXME(yukang): warning suppression From 6fead8f4fc3c5ae0458c2567c710b79ffb8ab0f9 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 22:20:37 +0800 Subject: [PATCH 18/30] Use deterministic rng for mock environment --- Cargo.lock | 4 ++-- src/ckb/chain.rs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d8c3788..78478a2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -950,7 +950,7 @@ checksum = "ae5937620517015b665ec7f703354017d687e5a4cccc6764379f90d90f112a6a" [[package]] name = "ckb-testtool" version = "0.11.0" -source = "git+https://github.com/contrun/capsule.git?branch=determinisitic-rng#d4d7361c93b7fb665ed3e4870abb6b8d4458f21c" +source = "git+https://github.com/contrun/capsule.git?branch=determinisitic-rng#bc787a9b6cd88d2d08856004e039dd8b6e802658" dependencies = [ "ckb-always-success-script", "ckb-chain-spec", @@ -1866,7 +1866,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.4.10", "tokio", "tower-service", "tracing", diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index ec6af926..fc652b4d 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -68,10 +68,18 @@ impl MockContext { pub fn new() -> Self { let mut context = Context::default(); - let (map, cell_deps) = Self::get_contract_binaries().into_iter().fold( + let (map, cell_deps) = Self::get_contract_binaries().into_iter().enumerate().fold( (HashMap::new(), vec![]), - |(mut map, mut cell_deps), (contract, binary)| { - let out_point = context.deploy_cell(binary); + |(mut map, mut cell_deps), (i, (contract, binary))| { + use ckb_hash::blake2b_256; + use rand::{rngs::StdRng, SeedableRng}; + let i = i + 123_456_789; + let seed = blake2b_256(i.to_le_bytes()); + let mut rng = StdRng::from_seed(seed); + // Use a deterministic RNG to ensure that the outpoints are the same for all nodes. + // Otherwise, cell deps may differ for different nodes, which would make + // different nodes sign different messages (as transaction hashes differ). + let out_point = context.deploy_cell_with_rng(binary, &mut rng); let script = context .build_script(&out_point, Default::default()) .expect("valid script"); From dd8fb997d05f5859701cb75fc1370a161acbbb4b Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 4 Jun 2024 23:37:49 +0800 Subject: [PATCH 19/30] Refine debug messages --- src/ckb/channel.rs | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 0c93bf39..e147fbf0 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -2605,10 +2605,10 @@ impl ChannelActorState { local: bool, commitment_number: u64, ) -> Vec { - dbg!( - "Building commitment transaction witnesses for commitment number", + debug!( + "Building commitment transaction #{}'s witnesses for {} party", commitment_number, - local + if local { "local" } else { "remote" } ); let (delayed_epoch, delayed_payment_key, revocation_key) = { let (delay, commitment_point, base_delayed_payment_key, base_revocation_key) = if local @@ -2629,7 +2629,7 @@ impl ChannelActorState { self.get_remote_channel_parameters().revocation_base_key(), ) }; - dbg!(delay, base_delayed_payment_key, base_revocation_key); + debug!("Get base witness parameters: delayed time: {:?}, delayed_payment_key: {:?}, revocation_key: {:?}", delay, base_delayed_payment_key, base_revocation_key); ( delay, derive_delayed_payment_pubkey(base_delayed_payment_key, &commitment_point), @@ -2670,16 +2670,11 @@ impl ChannelActorState { [a, b].concat() }; - let delayed_payment_key_hash = blake2b_256(delayed_payment_key.serialize()); - let revocation_key_hash = blake2b_256(revocation_key.serialize()); - - dbg!( - &tlcs, - local, - delayed_payment_key, - hex::encode(&delayed_payment_key_hash[..20]), - revocation_key, - hex::encode(&revocation_key_hash[..20]) + debug!( + "Get all tlcs for commitment tx #{} for {} party: {:?}", + commitment_number, + if local { "local" } else { "remote" }, + &tlcs ); let witnesses: Vec = [ (Since::from(delayed_epoch).value()).to_le_bytes().to_vec(), @@ -2690,7 +2685,21 @@ impl ChannelActorState { .flatten() .collect(), ] + .map(|x| { + debug!( + "Witness element for commitment transaction #{} of {} party: {:?}", + commitment_number, + if local { "local" } else { "remote" }, + hex::encode(&x) + ); + x + }) .concat(); + debug!( + "Built commitment transaction #{}'s witnesses: {:?}", + commitment_number, + hex::encode(&witnesses) + ); witnesses } From eb4bbdd7121ce9b9f8b6d409b746a64051c01458 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 00:25:40 +0800 Subject: [PATCH 20/30] Fix setting incorrect commitment tx witness --- src/ckb/channel.rs | 197 +++++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 77 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index e147fbf0..a2c09fb3 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -49,6 +49,10 @@ use super::{ NetworkActorCommand, NetworkActorEvent, NetworkActorMessage, }; +// - `version`: 8 bytes, u64 in little-endian +// - `funding_out_point`: 36 bytes, out point of the funding transaction +// - `pubkey`: 32 bytes, x only aggregated public key +// - `signature`: 64 bytes, aggregated signature const FUNDING_CELL_WITNESS_LEN: usize = 8 + 36 + 32 + 64; // Some part of the code liberally gets previous commitment number, which is // the current commitment number minus 1. We deliberately set initial commitment number to 1, @@ -232,7 +236,8 @@ impl ChannelActor { let PartiallySignedCommitmentTransaction { tx, signature, - num: _, + msg: _, + version: _, } = state.build_and_sign_commitment_tx()?; debug!( "Build a funding tx ({:?}) with partial signature {:?}", @@ -1832,35 +1837,44 @@ impl ChannelActorState { signature: CompactSignature, version: Option, ) -> [u8; FUNDING_CELL_WITNESS_LEN] { - // - `version`: 8 bytes, u64 in little-endian - // - `funding_out_point`: 36 bytes, out point of the funding transaction - // - `pubkey`: 32 bytes, x only aggregated public key - // - `signature`: 64 bytes, aggregated signature - let mut witness = Vec::with_capacity(FUNDING_CELL_WITNESS_LEN); - let xonly = self.get_funding_lock_script_xonly(); - let version = version.unwrap_or(u64::MAX); - for bytes in [ - version.to_le_bytes().as_ref(), - self.get_funding_transaction_outpoint().as_slice(), - xonly.as_slice(), - signature.serialize().as_slice(), - ] { - debug!( - "Extending witness with {} bytes: {:?}", - bytes.len(), - hex::encode(bytes) - ); - witness.extend_from_slice(bytes); - } + create_witness_for_funding_cell( + self.get_funding_lock_script_xonly(), + self.get_funding_transaction_outpoint(), + signature, + version, + ) + } + + pub fn sign_tx_to_consume_funding_cell( + &self, + tx: &PartiallySignedCommitmentTransaction, + ) -> Result { + let sign_ctx = Musig2SignContext::from(self); + let verify_ctx = Musig2VerifyContext::from(self); debug!( - "Building shutdown tx with witness: {:?}", - hex::encode(&witness) + "Signing and verifying commitment tx with message {:?}", + hex::encode(tx.msg.as_slice()) ); + let signature2 = sign_ctx.sign(tx.msg.as_slice())?; - witness - .try_into() - .expect("Witness length should be correct") + debug!( + "Signed commitment tx ({:?}) message {:?} with signature {:?}", + &tx.tx, &tx.msg, &signature2, + ); + let signature = aggregate_partial_signatures_for_msg( + tx.msg.as_slice(), + verify_ctx, + [tx.signature, signature2], + )?; + + let witness = self.create_witness_for_funding_cell(signature, Some(tx.version)); + let tx = tx + .tx + .as_advanced_builder() + .set_witnesses(vec![witness.pack()]) + .build(); + Ok(tx) } pub fn maybe_transition_to_shutdown( @@ -2571,18 +2585,35 @@ impl ChannelActorState { // signature from the other party), else we are building a commitment transaction // for the remote party (we build this commitment transaction // normally because we want to send a partial signature to remote). - pub fn build_commitment_tx(&self, local: bool) -> TransactionView { + // The function returns a tuple, the first element is the commitment transaction itself, + // and the second element is the message to be signed by the each party, + // so as to consume the funding cell. + pub fn build_commitment_tx(&self, local: bool) -> (TransactionView, [u8; 32]) { let commitment_lock_context = self.get_contracts_context(); - let input = self.get_funding_transaction_outpoint(); + let funding_out_point = self.get_funding_transaction_outpoint(); let tx_builder = TransactionBuilder::default() .cell_deps(commitment_lock_context.get_commitment_transaction_cell_deps()) - .input(CellInput::new_builder().previous_output(input).build()); + .input( + CellInput::new_builder() + .previous_output(funding_out_point.clone()) + .build(), + ); let (outputs, outputs_data) = self.build_commitment_transaction_outputs(local); debug!("Built outputs for commitment transaction: {:?}", &outputs); let tx_builder = tx_builder.set_outputs(outputs); let tx_builder = tx_builder.set_outputs_data(outputs_data); - tx_builder.build() + let tx = tx_builder.build(); + let message = get_funding_cell_message_to_sign( + Some(self.get_current_commitment_number(local)), + funding_out_point, + &tx, + ); + debug!( + "Building commitment transaction message to sign {:?}", + hex::encode(message.as_slice()) + ); + (tx, message) } fn build_previous_commitment_transaction_witnesses(&self, local: bool) -> Vec { @@ -2774,15 +2805,18 @@ impl ChannelActorState { let verify_ctx = Musig2VerifyContext::from(self); dbg!("Calling build_commitment_tx from build_and_verify_commitment_tx"); - let tx = self.build_commitment_tx(false); - let num = self.get_current_commitment_number(false); - let message = get_tx_message_to_sign(&tx); + let (tx, msg) = self.build_commitment_tx(false); debug!( "Verifying partial signature ({:?}) of commitment tx ({:?}) message {:?}", - &signature, &tx, &message + &signature, &tx, &msg ); - verify_ctx.verify(signature, message.as_slice())?; - Ok(PartiallySignedCommitmentTransaction { tx, signature, num }) + verify_ctx.verify(signature, msg.as_slice())?; + Ok(PartiallySignedCommitmentTransaction { + msg, + version: self.get_current_commitment_number(false), + tx, + signature, + }) } pub fn build_and_sign_commitment_tx( @@ -2790,21 +2824,24 @@ impl ChannelActorState { ) -> Result { let sign_ctx = Musig2SignContext::from(self); - let tx = self.build_commitment_tx(true); - let num = self.get_current_commitment_number(true); - let message = get_tx_message_to_sign(&tx); + let (tx, msg) = self.build_commitment_tx(true); debug!( "Signing commitment tx with message {:?}", - hex::encode(message.as_slice()) + hex::encode(msg.as_slice()) ); - let signature = sign_ctx.sign(message.as_slice())?; + let signature = sign_ctx.sign(msg.as_slice())?; debug!( "Signed commitment tx ({:?}) message {:?} with signature {:?}", - &tx, &message, &signature, + &tx, &msg, &signature, ); - Ok(PartiallySignedCommitmentTransaction { tx, signature, num }) + Ok(PartiallySignedCommitmentTransaction { + msg, + tx, + signature, + version: self.get_current_commitment_number(true), + }) } /// Verify the partial signature from the peer and create a complete transaction @@ -2815,40 +2852,10 @@ impl ChannelActorState { ) -> Result { dbg!("Calling build_commitment_tx from verify_and_complete_tx"); let tx = self.build_and_verify_commitment_tx(signature)?; - dbg!( - "verify_and_complete_tx build_and_verify_commitment_tx tx: {:?}", - &tx.tx, - tx.tx.hash(), - tx.tx.cell_deps(), - tx.tx.inputs(), - tx.tx.outputs(), - tx.tx.witnesses() - ); - let sign_ctx = Musig2SignContext::from(self); - - let message = get_tx_message_to_sign(&tx.tx); - - debug!( - "Signing and verifying commitment tx with message {:?}", - hex::encode(message.as_slice()) - ); - let signature2 = sign_ctx.sign(message.as_slice())?; - debug!( - "Signed commitment tx ({:?}) message {:?} with signature {:?}", - &tx, &message, &signature2, - ); - - let signatures = self.order_things_for_musig2(signature, signature2); - let tx = aggregate_partial_signatures_for_commitment_tx( - &tx.tx, - Musig2VerifyContext::from(&*self), - signatures, - ) - .expect("The validity of the signatures verified"); - dbg!("verify_and_complete_tx tx: {:?}", &tx); - Ok(tx) + self.sign_tx_to_consume_funding_cell(&tx) } } + pub trait ChannelActorStateStore { fn get_channel_actor_state(&self, id: &Hash256) -> Option; fn insert_channel_actor_state(&self, state: ChannelActorState); @@ -2860,14 +2867,50 @@ pub trait ChannelActorStateStore { /// the ckb transaction. #[derive(Clone, Debug)] pub struct PartiallySignedCommitmentTransaction { - // The commitment number of the commitment transaction. - pub num: u64, + // The message that was signed by the partial signature. + // This partial signature is going to be verified by the funding lock. + // We have to follow funding lock's rules to generate this message. + pub msg: [u8; 32], + // The version number of the commitment transaction. + pub version: u64, // The commitment transaction. pub tx: TransactionView, // The partial signature of the commitment transaction. pub signature: PartialSignature, } +pub fn create_witness_for_funding_cell( + lock_key_xonly: [u8; 32], + out_point: OutPoint, + signature: CompactSignature, + version: Option, +) -> [u8; FUNDING_CELL_WITNESS_LEN] { + let mut witness = Vec::with_capacity(FUNDING_CELL_WITNESS_LEN); + let version = version.unwrap_or(u64::MAX); + for bytes in [ + version.to_le_bytes().as_ref(), + out_point.as_slice(), + lock_key_xonly.as_slice(), + signature.serialize().as_slice(), + ] { + debug!( + "Extending witness with {} bytes: {:?}", + bytes.len(), + hex::encode(bytes) + ); + witness.extend_from_slice(bytes); + } + + debug!( + "Building shutdown tx with witness: {:?}", + hex::encode(&witness) + ); + + witness + .try_into() + .expect("Witness length should be correct") +} + pub struct Musig2Context { pub key_agg_ctx: KeyAggContext, pub agg_nonce: AggNonce, From 167f72c20ee53edf5f64d1474ac09a32c6f90dd2 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 00:44:02 +0800 Subject: [PATCH 21/30] Refactor shutdown tx creation --- src/ckb/channel.rs | 152 ++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 85 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index a2c09fb3..3adfefaa 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -1845,38 +1845,54 @@ impl ChannelActorState { ) } - pub fn sign_tx_to_consume_funding_cell( + pub fn aggregate_partial_signatures_to_consume_funding_cell( &self, - tx: &PartiallySignedCommitmentTransaction, + partial_signatures: [PartialSignature; 2], + version: Option, + tx: &TransactionView, ) -> Result { - let sign_ctx = Musig2SignContext::from(self); - let verify_ctx = Musig2VerifyContext::from(self); - + let funding_out_point = self.get_funding_transaction_outpoint(); + let message = get_funding_cell_message_to_sign(version, funding_out_point, tx); debug!( - "Signing and verifying commitment tx with message {:?}", - hex::encode(tx.msg.as_slice()) + "Get message to sign for funding tx {:?}", + hex::encode(message.as_slice()) ); - let signature2 = sign_ctx.sign(tx.msg.as_slice())?; - debug!( - "Signed commitment tx ({:?}) message {:?} with signature {:?}", - &tx.tx, &tx.msg, &signature2, - ); + let verify_ctx = Musig2VerifyContext::from(self); + let signature = aggregate_partial_signatures_for_msg( - tx.msg.as_slice(), + message.as_slice(), verify_ctx, - [tx.signature, signature2], + partial_signatures, )?; - let witness = self.create_witness_for_funding_cell(signature, Some(tx.version)); - let tx = tx - .tx + let witness = self.create_witness_for_funding_cell(signature, None); + let tx = self + .get_funding_transaction() .as_advanced_builder() .set_witnesses(vec![witness.pack()]) .build(); Ok(tx) } + pub fn sign_tx_to_consume_funding_cell( + &self, + tx: &PartiallySignedCommitmentTransaction, + ) -> Result { + debug!( + "Signing and verifying commitment tx with message {:?}", + hex::encode(tx.msg.as_slice()) + ); + let sign_ctx = Musig2SignContext::from(self); + let signature2 = sign_ctx.sign(tx.msg.as_slice())?; + + self.aggregate_partial_signatures_to_consume_funding_cell( + [tx.signature, signature2], + Some(tx.version), + &tx.tx, + ) + } + pub fn maybe_transition_to_shutdown( &mut self, network: ActorRef, @@ -1904,79 +1920,41 @@ impl ChannelActorState { flags | ShuttingDownFlags::DROPPING_PENDING, )); - let shutdown_tx = self.build_shutdown_tx()?; + let (shutdown_tx, message) = self.build_shutdown_tx()?; let sign_ctx = Musig2SignContext::from(&*self); // Create our shutdown signature if we haven't already. - match self.local_shutdown_signature { - None => { - let message = get_funding_cell_message_to_sign( - None, - self.get_funding_transaction_outpoint(), - &shutdown_tx, - ); - debug!( - "Building our shutdown signature for message {:?}", - hex::encode(message.as_slice()) - ); - let signature = sign_ctx.clone().sign(message.as_slice())?; - self.local_shutdown_signature = Some(signature); - debug!( - "We have signed shutdown tx ({:?}) message {:?} with signature {:?}", - &shutdown_tx, &message, &signature, - ); + let local_shutdown_signature = self.local_shutdown_signature.unwrap_or({ + let signature = sign_ctx.clone().sign(message.as_slice())?; + self.local_shutdown_signature = Some(signature); + debug!( + "We have signed shutdown tx ({:?}) message {:?} with signature {:?}", + &shutdown_tx, &message, &signature, + ); - network - .send_message(NetworkActorMessage::new_command( - NetworkActorCommand::SendPcnMessage(PCNMessageWithPeerId { - peer_id: self.peer_id.clone(), - message: PCNMessage::ClosingSigned(ClosingSigned { - partial_signature: signature, - channel_id: self.get_id(), - }), + network + .send_message(NetworkActorMessage::new_command( + NetworkActorCommand::SendPcnMessage(PCNMessageWithPeerId { + peer_id: self.peer_id.clone(), + message: PCNMessage::ClosingSigned(ClosingSigned { + partial_signature: signature, + channel_id: self.get_id(), }), - )) - .expect("network actor alive"); - } - Some(signatures) => { - debug!( - "We have already signed the shutdown tx signature {:?}", - &signatures - ); - } - } + }), + )) + .expect("network actor alive"); + signature + }); - match ( - self.local_shutdown_signature, - self.remote_shutdown_signature, - ) { - (Some(local_shutdown_signature), Some(remote_shutdown_signature)) => { + match self.remote_shutdown_signature { + Some(remote_shutdown_signature) => { self.update_state(ChannelState::Closed); - let partial_signatures = self - .order_things_for_musig2(local_shutdown_signature, remote_shutdown_signature); - let message = get_funding_cell_message_to_sign( + let tx = self.aggregate_partial_signatures_to_consume_funding_cell( + [local_shutdown_signature, remote_shutdown_signature], None, - self.get_funding_transaction_outpoint(), &shutdown_tx, - ); - debug!( - "Get message to sign for shutdown tx {:?}", - hex::encode(message.as_slice()) - ); - - let signature = aggregate_partial_signatures_for_msg( - message.as_slice(), - sign_ctx.into(), - partial_signatures, )?; - let witness = self.create_witness_for_funding_cell(signature, None); - - let tx = shutdown_tx - .as_advanced_builder() - .set_witnesses(vec![witness.pack()]) - .build(); - network .send_message(NetworkActorMessage::new_event( NetworkActorEvent::ChannelClosed(self.get_id(), self.peer_id.clone(), tx), @@ -1984,10 +1962,7 @@ impl ChannelActorState { .expect("network actor alive"); } - (None, _) => { - panic!("We should have our shutdown signature previously"); - } - (_, None) => { + None => { debug!("We have sent our shutdown signature, waiting for counterparty's signature"); } } @@ -2515,7 +2490,7 @@ impl ChannelActorState { && self.should_local_go_first_in_musig2() } - pub fn build_shutdown_tx(&self) -> Result { + pub fn build_shutdown_tx(&self) -> Result<(TransactionView, [u8; 32]), ProcessingChannelError> { // Don't use get_local_shutdown_script and get_remote_shutdown_script here // as they will panic if the scripts are not present. // This function may be called in a state where these scripts are not present. @@ -2576,7 +2551,14 @@ impl ChannelActorState { let outputs = self.order_things_for_musig2(local_output, remote_output); let tx_builder = tx_builder.set_outputs(outputs.to_vec()); let tx_builder = tx_builder.set_outputs_data(vec![Default::default(), Default::default()]); - Ok(tx_builder.build()) + let tx = tx_builder.build(); + let message = + get_funding_cell_message_to_sign(None, self.get_funding_transaction_outpoint(), &tx); + debug!( + "Building message to sign for shutdown transaction {:?}", + hex::encode(message.as_slice()) + ); + Ok((tx, message)) } // The parameter `local` here specifies whether we are building the commitment transaction From a82315f063a8ae94f050d6349f0e0dc869572664 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 00:59:32 +0800 Subject: [PATCH 22/30] Remove a few logs --- src/ckb/channel.rs | 16 ++++++---------- src/ckb/network.rs | 7 +++---- src/ckb/test_utils.rs | 4 ++-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 3adfefaa..c2cb0658 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -1355,10 +1355,10 @@ impl ChannelActorState { /// Get the counterparty commitment point for the given commitment number. pub fn get_remote_commitment_point(&self, commitment_number: u64) -> Pubkey { let index = commitment_number as usize; - dbg!( - "getting counterparty commitment point", + debug!( + "Obtaining {}th commitment point (out of {}) for remote", index, - &self.remote_commitment_points + self.remote_commitment_points.len() ); self.remote_commitment_points[index] } @@ -2355,7 +2355,7 @@ impl ChannelActorState { ))?; if first_output.lock() != self.get_funding_lock_script() { - dbg!(&tx, first_output.lock(), self.get_funding_lock_script()); + error!("Checking if transaction final failed as tx's first output's script is not funding lock: tx: {:?}, first output lock script: {:?}, funding lock script: {:?}", &tx, first_output.lock(), self.get_funding_lock_script()); // TODO: return an error here. We panic because we want to move fast. panic!("Invalid funding transation") } @@ -2604,10 +2604,8 @@ impl ChannelActorState { } else { self.remote_commitment_number - 1 }; - dbg!( - "Building previous commitment transaction witnesses for", - local, - commitment_number + debug!( + "Building previous commitment transaction witnesses for {} party, commitment number: {}", if local { "local" } else { "remote" }, commitment_number ); self.build_commitment_transaction_witnesses(local, commitment_number) } @@ -2786,7 +2784,6 @@ impl ChannelActorState { ) -> Result { let verify_ctx = Musig2VerifyContext::from(self); - dbg!("Calling build_commitment_tx from build_and_verify_commitment_tx"); let (tx, msg) = self.build_commitment_tx(false); debug!( "Verifying partial signature ({:?}) of commitment tx ({:?}) message {:?}", @@ -2832,7 +2829,6 @@ impl ChannelActorState { &self, signature: PartialSignature, ) -> Result { - dbg!("Calling build_commitment_tx from verify_and_complete_tx"); let tx = self.build_and_verify_commitment_tx(signature)?; self.sign_tx_to_consume_funding_cell(&tx) } diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 4b25167e..474b8946 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -145,7 +145,7 @@ pub enum NetworkServiceEvent { ChannelReady(PeerId, Hash256), ChannelShutDown(PeerId, Hash256), ChannelClosed(PeerId, Hash256, TransactionView), - // A CommitmentSigned message is pending to sent to the other party. + // We should sign a commitment transaction and send it to the other party. CommitmentSignaturePending(PeerId, Hash256, u64), // The other party has signed a valid commitment transaction, // and we successfully assemble the partial signature from other party @@ -1010,9 +1010,8 @@ where }); debug!("Starting funding channel"); - // TODO: Here we imply the one who receives AcceptChannel message - // will send TxUpdate message first. - dbg!(&script); + // TODO: Here we imply that the one who receives AcceptChannel message + // (i.e. the channel initiator) will send TxUpdate message first. myself .send_message(NetworkActorMessage::new_command( NetworkActorCommand::UpdateChannelFunding( diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index f7f3e194..7c6dcdcc 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -290,9 +290,9 @@ mod tests { #[tokio::test] async fn test_start_network_node() { - dbg!("start network node"); + println!("starting network node"); let node = NetworkNode::new().await; - dbg!("network node started", &node); + println!("network node {:?} started", &node); } #[tokio::test] From 4dad1f84c23ab097d9efeb3685545217d9d62444 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 01:02:01 +0800 Subject: [PATCH 23/30] Remove outdated test case --- src/ckb/channel.rs | 158 --------------------------------------------- 1 file changed, 158 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index c2cb0658..1cf26957 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -3342,13 +3342,6 @@ impl InMemorySigner { mod tests { use ckb_jsonrpc_types::Status; - use ckb_sdk::core::TransactionBuilder; - use ckb_types::{ - core::{DepType, ScriptHashType}, - packed::{CellDep, CellInput, CellOutput, OutPoint, Transaction}, - prelude::{AsTransactionBuilder, Pack}, - }; - use molecule::prelude::{Builder, Entity}; use crate::{ ckb::{ @@ -3380,157 +3373,6 @@ mod tests { assert_eq!(derived_privkey.pubkey(), derived_pubkey); } - // This test is used to dump transactions that are used in the tx collaboration of e2e tests. - // We generate the following transactions (tx1, tx2, tx3) by: - // 1. A add input1 to inputs, and outputs output1, call this tx tx1. - // 2. B add input2 to tx1, and outputs output1 and output2, call this tx tx2. - // 3. A remove input1 from tx2 and add input3 to tx2, keep outputs - // output1 and output2, call this tx tx3. - // TODO: change this into a unit test. - #[test] - fn test_dump_fake_tx_inputs_to_funding_tx() { - let always_success_out_point1 = ckb_types::packed::OutPoint::new_builder() - .tx_hash([0u8; 32].pack()) - .index(0u32.pack()) - .build(); - - let always_success_out_point2 = ckb_types::packed::OutPoint::new_builder() - .tx_hash([2u8; 32].pack()) - .index(0u32.pack()) - .build(); - - let funding_tx_lock_script_out_point = ckb_types::packed::OutPoint::new_builder() - .tx_hash([2u8; 32].pack()) - .index(0u32.pack()) - .build(); - - let secp256k1_code_hash = [42u8; 32]; - let secp256k1_hash_type = ScriptHashType::Data.into(); - - let funding_tx_lock_script = ckb_types::packed::Script::new_builder() - .code_hash([2u8; 32].pack()) - .hash_type(ScriptHashType::Data1.into()) - .args([3u8; 20].pack()) - .build(); - - let change1_lock_script = ckb_types::packed::Script::new_builder() - .code_hash(secp256k1_code_hash.pack()) - .hash_type(secp256k1_hash_type) - .args([1u8; 20].pack()) - .build(); - - let change2_lock_script = ckb_types::packed::Script::new_builder() - .code_hash(secp256k1_code_hash.pack()) - .hash_type(secp256k1_hash_type) - .args([2u8; 20].pack()) - .build(); - - let input1_capacity = 1000u64; - let input2_capacity = 2000u64; - let input3_capacity = 3000u64; - let output1_capacity = 500u64; - let output2_capacity = 1000u64; - - let build_input_cell = |capacity: u64, lock_script_outpoint: OutPoint| -> CellInput { - let mut tx_builder = TransactionBuilder::default(); - tx_builder - .cell_dep( - CellDep::new_builder() - .out_point(lock_script_outpoint) - .dep_type(DepType::Code.into()) - .build(), - ) - .output(CellOutput::new_builder().capacity(capacity.pack()).build()); - let tx = tx_builder.build(); - let outpoint = tx.output_pts_iter().next().unwrap(); - CellInput::new_builder().previous_output(outpoint).build() - }; - let tx1 = Transaction::default() - .as_advanced_builder() - .set_cell_deps(vec![ - CellDep::new_builder() - .out_point(funding_tx_lock_script_out_point.clone()) - .dep_type(DepType::Code.into()) - .build(), - CellDep::new_builder() - .out_point(always_success_out_point1.clone()) - .dep_type(DepType::Code.into()) - .build(), - ]) - .set_inputs(vec![build_input_cell( - input1_capacity, - always_success_out_point1.clone(), - )]) - .set_outputs(vec![ - CellOutput::new_builder() - .capacity(output1_capacity.pack()) - .lock(funding_tx_lock_script.clone()) - .build(), - CellOutput::new_builder() - .capacity((input1_capacity - output1_capacity).pack()) - .lock(change1_lock_script.clone()) - .build(), - ]) - .build(); - dbg!(&tx1); - let tx2 = { - let inputs = tx1.inputs().into_iter().chain( - vec![build_input_cell( - input2_capacity, - always_success_out_point2.clone(), - )] - .into_iter(), - ); - let cell_deps = tx1.cell_deps().into_iter().chain( - vec![CellDep::new_builder() - .out_point(always_success_out_point2.clone()) - .dep_type(DepType::Code.into()) - .build()] - .into_iter(), - ); - Transaction::default() - .as_advanced_builder() - .set_cell_deps(cell_deps.collect()) - .set_inputs(inputs.collect()) - .set_outputs(vec![ - CellOutput::new_builder() - .capacity((output1_capacity + output2_capacity).pack()) - .lock(funding_tx_lock_script.clone()) - .build(), - CellOutput::new_builder() - .capacity((input1_capacity - output1_capacity).pack()) - .lock(change1_lock_script.clone()) - .build(), - CellOutput::new_builder() - .capacity((input2_capacity - output2_capacity).pack()) - .lock(change2_lock_script.clone()) - .build(), - ]) - .build() - }; - dbg!(&tx2); - - let tx3 = { - let inputs = tx2 - .inputs() - .into_iter() - .skip(1) - .chain(vec![build_input_cell( - input3_capacity, - always_success_out_point2, - )]); - let cell_deps = tx2.cell_deps().into_iter().skip(1); - let outputs = tx2.outputs().into_iter(); - Transaction::default() - .as_advanced_builder() - .set_cell_deps(cell_deps.collect()) - .set_inputs(inputs.collect()) - .set_outputs(outputs.collect()) - .build() - }; - dbg!(&tx3); - } - #[tokio::test] async fn test_open_channel_to_peer() { let [node_a, mut node_b] = NetworkNode::new_n_interconnected_nodes(2) From aadbacc7b499553e4475a94d9bbc73ccfcd5f353 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 01:04:07 +0800 Subject: [PATCH 24/30] Remove unused code --- src/ckb/channel.rs | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index 1cf26957..dcd9682f 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -3,7 +3,7 @@ use ckb_hash::{blake2b_256, new_blake2b}; use ckb_sdk::Since; use ckb_types::{ core::{TransactionBuilder, TransactionView}, - packed::{Byte32, Bytes, CellInput, CellOutput, OutPoint, Script, Transaction}, + packed::{Bytes, CellInput, CellOutput, OutPoint, Script, Transaction}, prelude::{AsTransactionBuilder, IntoTransactionView, Pack, Unpack}, }; @@ -12,8 +12,8 @@ use molecule::prelude::{Builder, Entity}; use musig2::{ aggregate_partial_signatures, errors::{SigningError, VerifyError}, - sign_partial, verify_partial, AggNonce, BinaryEncoding, CompactSignature, KeyAggContext, - PartialSignature, PubNonce, SecNonce, + sign_partial, verify_partial, AggNonce, CompactSignature, KeyAggContext, PartialSignature, + PubNonce, SecNonce, }; use ractor::{ async_trait as rasync_trait, Actor, ActorProcessingErr, ActorRef, RpcReplyPort, SpawnErr, @@ -2977,11 +2977,6 @@ impl Musig2SignContext { } } -fn get_tx_message_to_sign(tx: &TransactionView) -> Byte32 { - let hash = tx.hash(); - hash -} - fn get_funding_cell_message_to_sign( version: Option, funding_out_point: OutPoint, @@ -3013,23 +3008,6 @@ pub fn aggregate_partial_signatures_for_msg( Ok(signature) } -pub fn aggregate_partial_signatures_for_commitment_tx( - tx: &TransactionView, - verify_ctx: Musig2VerifyContext, - partial_signatures: [PartialSignature; 2], -) -> Result { - debug!("Aggregating partial signatures for tx {:?}", tx); - let message = get_tx_message_to_sign(tx); - - let signature = - aggregate_partial_signatures_for_msg(message.as_slice(), verify_ctx, partial_signatures)?; - - Ok(tx - .as_advanced_builder() - .set_witnesses(vec![signature.to_bytes().pack()]) - .build()) -} - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ChannelParametersOneParty { pub pubkeys: ChannelBasePublicKeys, From d65cf3dabd8d10c7b5349c588d3c42ee63763b80 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 01:25:06 +0800 Subject: [PATCH 25/30] Update outdated comment --- src/ckb/chain.rs | 3 --- src/ckb_chain/actor.rs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ckb/chain.rs b/src/ckb/chain.rs index fc652b4d..47e09da5 100644 --- a/src/ckb/chain.rs +++ b/src/ckb/chain.rs @@ -26,9 +26,6 @@ pub struct MockContext { #[cfg(test)] impl MockContext { - // If we are using cfg(test), then directly including contracts binaries into the - // resulting executable is not a problem. Otherwise, we'd better read the binaries from - // the filesystem. fn get_contract_binaries() -> Vec<(Contract, Bytes)> { [ ( diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 4ec74a50..2712a4c0 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -344,7 +344,7 @@ mod test_utils { } Sign(tx, reply_port) => { // We don't need to sign the funding transaction in mock chain actor, - // as any funding transaction is considered correct it we can successfully + // as any funding transaction is considered correct if we can successfully // run the scripts of transaction inputs, and we don't have inputs in the // funding transaction. let signed_tx = tx.clone(); From 6384fbb052421da63c1cb0d8d494d1e0778e3a1a Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 01:26:16 +0800 Subject: [PATCH 26/30] Move ckb-testtool to dev dependencies --- Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index beb23123..31a6ad79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,9 @@ molecule = { version = "0.7.5", default-features = false } ckb-types = "0.114.0" ckb-jsonrpc-types = "0.114.0" ckb-crypto = "0.114.0" -rocksdb = { package = "ckb-rocksdb", version ="=0.21.1", features = ["snappy"], default-features = false } +rocksdb = { package = "ckb-rocksdb", version = "=0.21.1", features = [ + "snappy", +], default-features = false } serde_with = { version = "3.7.0", features = ["macros", "base64"] } hex = "0.4.3" tower = "0.4.13" @@ -63,7 +65,6 @@ musig2 = { version = "0.0.11", features = ["secp256k1", "serde"] } ractor = "0.9.7" arcode = "0.2.4" nom = "7.1.3" -ckb-testtool = { git = "https://github.com/contrun/capsule.git", branch = "determinisitic-rng" } [profile.release] panic = "abort" @@ -73,3 +74,4 @@ panic = "abort" [dev-dependencies] tempfile = "3.10.1" +ckb-testtool = { git = "https://github.com/contrun/capsule.git", branch = "determinisitic-rng" } From 9037cc4483d1b8855b7f25b1f7b7543285720883 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 02:47:04 +0800 Subject: [PATCH 27/30] Move functions to obtain contract scripts/cell_deps to ckb_chain --- src/ckb/channel.rs | 45 +++++--------- src/ckb/mod.rs | 2 - src/ckb/network.rs | 35 +++-------- src/ckb/test_utils.rs | 9 +-- src/ckb_chain/actor.rs | 30 ++++++--- src/{ckb/chain.rs => ckb_chain/contracts.rs} | 64 ++++++++++---------- src/ckb_chain/mod.rs | 1 + src/main.rs | 9 +-- 8 files changed, 82 insertions(+), 113 deletions(-) rename src/{ckb/chain.rs => ckb_chain/contracts.rs} (90%) diff --git a/src/ckb/channel.rs b/src/ckb/channel.rs index dcd9682f..874f4ac9 100644 --- a/src/ckb/channel.rs +++ b/src/ckb/channel.rs @@ -32,8 +32,11 @@ use std::{ }; use crate::{ - ckb::{chain::CommitmentLockContext, types::Shutdown}, - ckb_chain::FundingRequest, + ckb::types::Shutdown, + ckb_chain::{ + contracts::{get_cell_deps_by_contracts, get_script_by_contract, Contract}, + FundingRequest, + }, NetworkServiceEvent, RpcError, }; @@ -567,12 +570,12 @@ where { type Msg = ChannelActorMessage; type State = ChannelActorState; - type Arguments = (CommitmentLockContext, ChannelInitializationParameter); + type Arguments = ChannelInitializationParameter; async fn pre_start( &self, myself: ActorRef, - (ctx, args): Self::Arguments, + args: Self::Arguments, ) -> Result { // startup the event processing match args { @@ -627,7 +630,6 @@ where next_local_nonce.clone(), *first_per_commitment_point, *second_per_commitment_point, - ctx, ); let commitment_number = 0; @@ -690,7 +692,6 @@ where self.peer_id.clone(), funding_amount, LockTime::new(DEFAULT_TO_LOCAL_DELAY_BLOCKS), - ctx, ); let commitment_number = 0; @@ -945,11 +946,6 @@ pub struct ChannelActorState { pub local_shutdown_fee: Option, pub remote_shutdown_signature: Option, pub remote_shutdown_fee: Option, - - // Providing additional information about contracts (e.g. commitmemt lock outpoint) - // that are used in pcn channels. - #[serde(skip)] - pub contracts_context: Option, } #[derive(PartialEq, Eq, Clone, Debug)] @@ -1140,7 +1136,6 @@ impl ChannelActorState { remote_nonce: PubNonce, remote_commitment_point: Pubkey, remote_prev_commitment_point: Pubkey, - contracts_context: CommitmentLockContext, ) -> Self { let signer = InMemorySigner::generate_from_seed(seed); let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); @@ -1186,7 +1181,6 @@ impl ChannelActorState { local_shutdown_fee: None, remote_shutdown_signature: None, remote_shutdown_fee: None, - contracts_context: Some(contracts_context), } } @@ -1195,7 +1189,6 @@ impl ChannelActorState { peer_id: PeerId, value: u128, to_local_delay: LockTime, - contracts_context: CommitmentLockContext, ) -> Self { let signer = InMemorySigner::generate_from_seed(seed); let local_pubkeys = signer.to_channel_public_keys(INITIAL_COMMITMENT_NUMBER); @@ -1229,7 +1222,6 @@ impl ChannelActorState { remote_shutdown_fee: None, local_shutdown_signature: None, remote_shutdown_signature: None, - contracts_context: Some(contracts_context), } } @@ -1248,10 +1240,6 @@ impl ChannelActorState { self.id } - pub fn get_contracts_context(&self) -> &CommitmentLockContext { - self.contracts_context.as_ref().unwrap() - } - pub fn get_local_nonce(&self) -> impl Borrow { self.get_next_local_secnonce().public_nonce() } @@ -1369,14 +1357,13 @@ impl ChannelActorState { } pub fn get_funding_lock_script(&self) -> Script { - let ctx: &CommitmentLockContext = self.get_contracts_context(); let args = blake2b_256(self.get_funding_lock_script_xonly()); debug!( "Aggregated pubkey: {:?}, hash: {:?}", hex::encode(&args), hex::encode(&args[..20]) ); - ctx.get_funding_lock_script(&args[..20]) + get_script_by_contract(Contract::FundingLock, args.as_slice()) } pub fn get_funding_request(&self, fee_rate: u64) -> FundingRequest { @@ -2524,9 +2511,8 @@ impl ChannelActorState { ))); } }; - let commitment_lock_context = self.get_contracts_context(); let tx_builder = TransactionBuilder::default() - .cell_deps(commitment_lock_context.get_commitment_transaction_cell_deps()) + .cell_deps(get_cell_deps_by_contracts(vec![Contract::CommitmentLock])) .input( CellInput::new_builder() .previous_output(self.get_funding_transaction_outpoint()) @@ -2571,10 +2557,9 @@ impl ChannelActorState { // and the second element is the message to be signed by the each party, // so as to consume the funding cell. pub fn build_commitment_tx(&self, local: bool) -> (TransactionView, [u8; 32]) { - let commitment_lock_context = self.get_contracts_context(); let funding_out_point = self.get_funding_transaction_outpoint(); let tx_builder = TransactionBuilder::default() - .cell_deps(commitment_lock_context.get_commitment_transaction_cell_deps()) + .cell_deps(get_cell_deps_by_contracts(vec![Contract::CommitmentLock])) .input( CellInput::new_builder() .previous_output(funding_out_point.clone()) @@ -2757,11 +2742,12 @@ impl ChannelActorState { self.remote_commitment_number ); - let commitment_lock_ctx = self.get_contracts_context(); - let immediate_secp256k1_lock_script = commitment_lock_ctx - .get_secp256k1_lock_script(&blake2b_256(immediate_payment_key.serialize())[0..20]); + let immediate_secp256k1_lock_script = get_script_by_contract( + Contract::Secp256k1Lock, + &blake2b_256(immediate_payment_key.serialize())[0..20], + ); let commitment_lock_script = - commitment_lock_ctx.get_commitment_lock_script(&blake2b_256(witnesses)[0..20]); + get_script_by_contract(Contract::CommitmentLock, &blake2b_256(witnesses)[0..20]); let outputs = vec![ CellOutput::new_builder() @@ -3637,6 +3623,7 @@ mod tests { }) .await; + // We can submit the commitment txs to the chain now. assert_eq!( node_a.submit_tx(node_a_commitment_tx.clone()).await, Status::Committed diff --git a/src/ckb/mod.rs b/src/ckb/mod.rs index 5a0798f8..2c77fc69 100644 --- a/src/ckb/mod.rs +++ b/src/ckb/mod.rs @@ -20,5 +20,3 @@ pub mod serde_utils; #[cfg(test)] pub mod test_utils; - -pub mod chain; diff --git a/src/ckb/network.rs b/src/ckb/network.rs index 474b8946..5242d0be 100644 --- a/src/ckb/network.rs +++ b/src/ckb/network.rs @@ -33,7 +33,6 @@ use tentacle::{ use tokio::sync::{mpsc, oneshot}; use tokio_util::task::TaskTracker; -use super::chain::CommitmentLockContext; use super::channel::{ ChannelActorMessage, ChannelActorStateStore, ChannelCommandWithId, ChannelEvent, ProcessingChannelError, ProcessingChannelResult, @@ -230,8 +229,6 @@ pub struct NetworkActor { // An event emitter to notify ourside observers. event_sender: mpsc::Sender, chain_actor: ActorRef, - // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. - ctx: CommitmentLockContext, store: S, } @@ -242,14 +239,11 @@ where pub fn new( event_sender: mpsc::Sender, chain_actor: ActorRef, - // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. - ctx: CommitmentLockContext, store: S, ) -> Self { Self { event_sender, chain_actor, - ctx, store, } } @@ -541,7 +535,6 @@ pub struct NetworkActorState { pending_channels: HashMap, // Used to broadcast and query network info. chain_actor: ActorRef, - contracts_context: CommitmentLockContext, } impl NetworkActorState { @@ -572,10 +565,7 @@ impl NetworkActorState { let channel = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), network.clone(), store), - ( - self.contracts_context.clone(), - ChannelInitializationParameter::OpenChannel(funding_amount, seed, tx), - ), + ChannelInitializationParameter::OpenChannel(funding_amount, seed, tx), network.clone().get_cell(), ) .await? @@ -612,14 +602,11 @@ impl NetworkActorState { let channel = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), network.clone(), store), - ( - self.contracts_context.clone(), - ChannelInitializationParameter::AcceptChannel( - funding_amount, - seed, - open_channel, - Some(tx), - ), + ChannelInitializationParameter::AcceptChannel( + funding_amount, + seed, + open_channel, + Some(tx), ), network.clone().get_cell(), ) @@ -683,10 +670,7 @@ impl NetworkActorState { if let Ok((channel, _)) = Actor::spawn_linked( None, ChannelActor::new(peer_id.clone(), self.network.clone(), store.clone()), - ( - self.contracts_context.clone(), - ChannelInitializationParameter::ReestablishChannel(channel_id), - ), + ChannelInitializationParameter::ReestablishChannel(channel_id), self.network.get_cell(), ) .await @@ -946,7 +930,6 @@ where to_be_accepted_channels: Default::default(), pending_channels: Default::default(), chain_actor: self.chain_actor.clone(), - contracts_context: self.ctx.clone(), }) } @@ -1250,13 +1233,11 @@ pub async fn start_ckb, tracker: TaskTracker, root_actor: ActorCell, - // TODO: The functionality of CommitmentLockContext should be merged to chain_actor. - ctx: CommitmentLockContext, store: S, ) -> ActorRef { let (actor, _handle) = Actor::spawn_linked( Some("network actor".to_string()), - NetworkActor::new(event_sender, chain_actor, ctx, store), + NetworkActor::new(event_sender, chain_actor, store), (config, tracker), root_actor, ) diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index 7c6dcdcc..312f1487 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -22,7 +22,6 @@ use tokio::{ use crate::{ actors::{RootActor, RootActorMessage}, - ckb::{chain::CommitmentLockContext, config::CkbNetwork}, ckb_chain::{CkbChainMessage, MockChainActor, TraceTxRequest}, tasks::{new_tokio_cancellation_token, new_tokio_task_tracker}, CkbConfig, NetworkServiceEvent, @@ -105,7 +104,6 @@ impl NetworkNode { ..Default::default() }; - let ctx = CommitmentLockContext::new(CkbNetwork::Mocknet); let root = ROOT_ACTOR.get_or_init(get_test_root_actor).await.clone(); let (event_sender, mut event_receiver) = mpsc::channel(10000); @@ -119,12 +117,7 @@ impl NetworkNode { let network_actor = Actor::spawn_linked( Some(format!("network actor at {:?}", base_dir.as_ref())), - NetworkActor::new( - event_sender, - chain_actor.clone(), - ctx, - MemoryStore::default(), - ), + NetworkActor::new(event_sender, chain_actor.clone(), MemoryStore::default()), (ckb_config, new_tokio_task_tracker()), root.get_cell(), ) diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 2712a4c0..3da0a13b 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -1,11 +1,15 @@ use ckb_sdk::{CkbRpcClient, RpcError}; -use ckb_types::{core::TransactionView, packed, prelude::*}; +use ckb_types::{ + core::TransactionView, + packed::{self}, + prelude::*, +}; use ractor::{ concurrency::{sleep, Duration}, Actor, ActorProcessingErr, ActorRef, RpcReplyPort, }; -use crate::ckb::chain::CommitmentLockContext; +use crate::ckb_chain::contracts::{get_script_by_contract, Contract}; use super::{funding::FundingContext, CkbChainConfig, FundingError, FundingRequest, FundingTx}; @@ -17,7 +21,6 @@ pub struct CkbChainState { config: CkbChainConfig, secret_key: secp256k1::SecretKey, funding_source_lock_script: packed::Script, - ctx: CommitmentLockContext, } #[derive(Debug, Clone)] @@ -27,6 +30,17 @@ pub struct TraceTxRequest { pub confirmations: u64, } +#[derive(Debug, Clone)] +pub struct GetScriptRequest { + pub contract: super::contracts::Contract, + pub args: Vec, +} + +#[derive(Debug, Clone)] +pub struct GetCellDepsRequest { + pub contracts: Vec, +} + #[derive(Debug)] pub enum CkbChainMessage { Fund( @@ -43,19 +57,20 @@ pub enum CkbChainMessage { impl Actor for CkbChainActor { type Msg = CkbChainMessage; type State = CkbChainState; - type Arguments = (CkbChainConfig, CommitmentLockContext); + type Arguments = CkbChainConfig; async fn pre_start( &self, myself: ActorRef, - (config, ctx): Self::Arguments, + config: Self::Arguments, ) -> Result { let secret_key = config.read_secret_key()?; let secp = secp256k1::Secp256k1::new(); let pub_key = secret_key.public_key(&secp); let pub_key_hash = ckb_hash::blake2b_256(pub_key.serialize()); - let funding_source_lock_script = ctx.get_secp256k1_lock_script(&pub_key_hash[0..20]); + let funding_source_lock_script = + get_script_by_contract(Contract::Secp256k1Lock, &pub_key_hash[0..20]); log::info!( "[{}] funding lock args: {}", myself.get_name().unwrap_or_default(), @@ -66,7 +81,6 @@ impl Actor for CkbChainActor { config, secret_key, funding_source_lock_script, - ctx, }) } @@ -225,8 +239,8 @@ mod test_utils { prelude::{Builder, Entity, Pack, PackVec, Unpack}, }; + use super::super::contracts::MockContext; use super::CkbChainMessage; - use crate::ckb::chain::MockContext; use ckb_types::packed::Byte32; use log::{debug, error}; diff --git a/src/ckb/chain.rs b/src/ckb_chain/contracts.rs similarity index 90% rename from src/ckb/chain.rs rename to src/ckb_chain/contracts.rs index 47e09da5..42e29dc7 100644 --- a/src/ckb/chain.rs +++ b/src/ckb_chain/contracts.rs @@ -7,7 +7,7 @@ use log::debug; use std::{collections::HashMap, env, str::FromStr, sync::Arc}; -use super::{config::CkbNetwork, types::Hash256}; +use crate::ckb::{config::CkbNetwork, types::Hash256}; #[cfg(not(test))] use ckb_types::bytes::Bytes; @@ -21,7 +21,7 @@ use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; #[derive(Clone, Debug)] pub struct MockContext { context: Arc>, - contracts_context: Arc, + contracts_context: Arc, } #[cfg(test)] @@ -99,7 +99,7 @@ impl MockContext { let context = MockContext { context: Arc::new(RwLock::new(context)), - contracts_context: Arc::new(ContractsContext { + contracts_context: Arc::new(ContractsInfo { contract_default_scripts: map, cell_deps: cell_dep_vec, }), @@ -118,7 +118,7 @@ impl MockContext { } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -enum Contract { +pub enum Contract { FundingLock, CommitmentLock, Secp256k1Lock, @@ -129,20 +129,21 @@ enum Contract { } #[derive(Clone, Debug)] -pub struct ContractsContext { +struct ContractsInfo { contract_default_scripts: HashMap, // TODO: We bundle all the cell deps together, but some of they are not always needed. cell_deps: CellDepVec, } #[derive(Clone)] -pub enum CommitmentLockContext { +pub enum ContractsContext { #[cfg(test)] Mock(MockContext), - Real(Arc), + #[allow(private_interfaces)] + Real(Arc), } -impl std::fmt::Debug for CommitmentLockContext { +impl std::fmt::Debug for ContractsContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { #[cfg(test)] @@ -212,9 +213,7 @@ fn get_environment_variable( ) } -impl CommitmentLockContext { - // TODO: better way to organize this? Currently CommitmentLockContext is a singleton - // because it is used in so many places. +impl ContractsContext { pub fn new(network: CkbNetwork) -> Self { match network { #[cfg(test)] @@ -304,7 +303,7 @@ impl CommitmentLockContext { "Use these contracts by specifying cell deps to {:?}", &cell_dep_vec ); - Self::Real(Arc::new(ContractsContext { + Self::Real(Arc::new(ContractsInfo { contract_default_scripts: map, cell_deps: cell_dep_vec, })) @@ -372,7 +371,7 @@ impl CommitmentLockContext { .build() }) .pack(); - Self::Real(Arc::new(ContractsContext { + Self::Real(Arc::new(ContractsInfo { contract_default_scripts: map, cell_deps: cell_dep_vec.pack(), })) @@ -388,15 +387,15 @@ impl CommitmentLockContext { Self::Real(real) => &real.contract_default_scripts, } } - fn get_cell_deps(&self) -> &CellDepVec { + pub(crate) fn get_cell_deps(&self, _contracts: Vec) -> CellDepVec { match self { #[cfg(test)] - Self::Mock(mock) => &mock.contracts_context.cell_deps, - Self::Real(real) => &real.cell_deps, + Self::Mock(mock) => mock.contracts_context.cell_deps.clone(), + Self::Real(real) => real.cell_deps.clone(), } } - fn get_script(&self, contract: Contract, args: &[u8]) -> Script { + pub(crate) fn get_script(&self, contract: Contract, args: &[u8]) -> Script { self.get_contracts_map() .get(&contract) .expect(format!("Contract {:?} exists", contract).as_str()) @@ -405,24 +404,23 @@ impl CommitmentLockContext { .args(args.pack()) .build() } +} - pub fn get_secp256k1_lock_script(&self, args: &[u8]) -> Script { - self.get_script(Contract::Secp256k1Lock, args) - } - - pub fn get_funding_lock_script(&self, args: &[u8]) -> Script { - self.get_script(Contract::FundingLock, args) - } +pub fn init_contracts_context(network: Option) -> &'static ContractsContext { + static INSTANCE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + INSTANCE.get_or_init(|| ContractsContext::new(network.unwrap_or(DEFAULT_CONTRACT_NETWORK))); + INSTANCE.get().unwrap() +} - pub fn get_commitment_lock_script(&self, args: &[u8]) -> Script { - self.get_script(Contract::CommitmentLock, args) - } +#[cfg(test)] +const DEFAULT_CONTRACT_NETWORK: CkbNetwork = CkbNetwork::Mocknet; +#[cfg(not(test))] +const DEFAULT_CONTRACT_NETWORK: CkbNetwork = CkbNetwork::Dev; - pub fn get_commitment_transaction_cell_deps(&self) -> CellDepVec { - self.get_cell_deps().clone() - } +pub fn get_script_by_contract(contract: Contract, args: &[u8]) -> Script { + init_contracts_context(None).get_script(contract, args) +} - pub fn get_always_success_script(&self, args: &[u8]) -> Script { - self.get_script(Contract::AlwaysSuccess, args) - } +pub fn get_cell_deps_by_contracts(contracts: Vec) -> CellDepVec { + init_contracts_context(None).get_cell_deps(contracts) } diff --git a/src/ckb_chain/mod.rs b/src/ckb_chain/mod.rs index f23e586d..5e8df5d3 100644 --- a/src/ckb_chain/mod.rs +++ b/src/ckb_chain/mod.rs @@ -10,3 +10,4 @@ pub use funding::{FundingRequest, FundingTx, FundingUdtInfo}; #[cfg(test)] pub use actor::MockChainActor; +pub mod contracts; diff --git a/src/main.rs b/src/main.rs index 5768f34e..c5a05177 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ -use ckb_pcn_node::ckb::chain::CommitmentLockContext; -use ckb_pcn_node::ckb::config::CkbNetwork; +use ckb_pcn_node::ckb_chain::contracts::init_contracts_context; use ckb_pcn_node::invoice::start_invoice; use ckb_pcn_node::rpc::InvoiceCommandWithReply; use ckb_pcn_node::store::Store; @@ -52,13 +51,12 @@ pub async fn main() { // for the user to fix the error and start the node. let ckb_chain_config = config.ckb_chain.expect("ckb-chain service is required for ckb service. Add ckb-chain service to the services list in the config file and relevant configuration to the ckb_chain section of the config file."); - let network = ckb_config.network.unwrap_or(CkbNetwork::Dev); - let ctx = CommitmentLockContext::new(network); + let _ = init_contracts_context(ckb_config.network); let ckb_chain_actor = Actor::spawn_linked( Some("ckb-chain".to_string()), CkbChainActor {}, - (ckb_chain_config, ctx.clone()), + ckb_chain_config, root_actor.get_cell(), ) .await @@ -77,7 +75,6 @@ pub async fn main() { event_sender, new_tokio_task_tracker(), root_actor.get_cell(), - ctx, store.clone(), ) .await; From 48e7ec73f62fd016d82a6bc8efb99d1a3be508e0 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 04:53:42 +0800 Subject: [PATCH 28/30] Add more tests to mock ckb chain actor --- src/ckb/test_utils.rs | 22 +----- src/ckb_chain/actor.rs | 161 ++++++++++++++++++++++++++++++++++++++++- src/ckb_chain/mod.rs | 2 +- 3 files changed, 163 insertions(+), 22 deletions(-) diff --git a/src/ckb/test_utils.rs b/src/ckb/test_utils.rs index 312f1487..d89ece37 100644 --- a/src/ckb/test_utils.rs +++ b/src/ckb/test_utils.rs @@ -10,7 +10,7 @@ use std::{ use ckb_types::core::TransactionView; -use ractor::{call_t, Actor, ActorRef}; +use ractor::{Actor, ActorRef}; use tempfile::TempDir as OldTempDir; use tentacle::{multiaddr::MultiAddr, secio::PeerId}; @@ -22,7 +22,7 @@ use tokio::{ use crate::{ actors::{RootActor, RootActorMessage}, - ckb_chain::{CkbChainMessage, MockChainActor, TraceTxRequest}, + ckb_chain::{submit_tx, CkbChainMessage, MockChainActor}, tasks::{new_tokio_cancellation_token, new_tokio_task_tracker}, CkbConfig, NetworkServiceEvent, }; @@ -216,23 +216,7 @@ impl NetworkNode { } pub async fn submit_tx(&mut self, tx: TransactionView) -> ckb_jsonrpc_types::Status { - pub const TIMEOUT: u64 = 1000; - let tx_hash = tx.hash(); - - self.chain_actor - .send_message(CkbChainMessage::SendTx(tx)) - .expect("chain actor alive"); - let request = TraceTxRequest { - tx_hash, - confirmations: 1, - }; - call_t!( - self.chain_actor, - CkbChainMessage::TraceTx, - TIMEOUT, - request.clone() - ) - .expect("chain actor alive") + submit_tx(self.chain_actor.clone(), tx).await } } diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index 3da0a13b..d2d41e8f 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -228,23 +228,26 @@ impl CkbChainState { } #[cfg(test)] -pub use test_utils::MockChainActor; +pub use test_utils::{submit_tx, MockChainActor}; #[cfg(test)] mod test_utils { use std::collections::HashMap; use ckb_types::{ + core::TransactionView, packed::CellOutput, prelude::{Builder, Entity, Pack, PackVec, Unpack}, }; + use crate::ckb_chain::TraceTxRequest; + use super::super::contracts::MockContext; use super::CkbChainMessage; use ckb_types::packed::Byte32; use log::{debug, error}; - use ractor::{Actor, ActorProcessingErr, ActorRef}; + use ractor::{call_t, Actor, ActorProcessingErr, ActorRef}; pub struct MockChainActorState { ctx: MockContext, @@ -428,4 +431,158 @@ mod test_utils { Ok(()) } } + + pub async fn submit_tx( + mock_actor: ActorRef, + tx: TransactionView, + ) -> ckb_jsonrpc_types::Status { + pub const TIMEOUT: u64 = 1000; + let tx_hash = tx.hash(); + + mock_actor + .send_message(CkbChainMessage::SendTx(tx)) + .expect("chain actor alive"); + let request = TraceTxRequest { + tx_hash, + confirmations: 1, + }; + call_t!( + mock_actor, + CkbChainMessage::TraceTx, + TIMEOUT, + request.clone() + ) + .expect("chain actor alive") + } +} + +#[cfg(test)] +mod test { + use ckb_jsonrpc_types::Status; + use ckb_types::core::TransactionView; + use ckb_types::packed::{CellInput, CellOutput}; + use ckb_types::prelude::{Builder, Pack}; + use molecule::prelude::Entity; + use ractor::{Actor, ActorRef}; + + use super::super::contracts::{get_cell_deps_by_contracts, get_script_by_contract, Contract}; + use super::test_utils::submit_tx; + use super::CkbChainMessage; + + async fn create_mock_chain_actor() -> ActorRef { + use super::test_utils::MockChainActor; + + let chain_actor = Actor::spawn(None, MockChainActor::new(), ()) + .await + .expect("start mock chain actor") + .0; + chain_actor + } + + #[tokio::test] + async fn test_submit_empty_tx() { + let actor = create_mock_chain_actor().await; + assert_eq!( + submit_tx(actor, TransactionView::new_advanced_builder().build()).await, + Status::Committed + ); + } + + #[tokio::test] + async fn test_submit_one_output_tx() { + let actor = create_mock_chain_actor().await; + assert_eq!( + submit_tx( + actor, + TransactionView::new_advanced_builder() + .output(CellOutput::default()) + .output_data(Default::default()) + .build() + ) + .await, + Status::Committed + ); + } + + #[tokio::test] + async fn test_submit_always_success_tx() { + let actor = create_mock_chain_actor().await; + let capacity = 100u64; + let output = CellOutput::new_builder() + .capacity(capacity.pack()) + .lock(get_script_by_contract( + Contract::AlwaysSuccess, + &b"whatever1"[..], + )) + .build(); + let tx = TransactionView::new_advanced_builder() + .output(output) + .output_data(Default::default()) + .build(); + assert_eq!( + submit_tx(actor.clone(), tx.clone()).await, + Status::Committed + ); + let out_point = tx.output_pts_iter().next().unwrap(); + let tx = TransactionView::new_advanced_builder() + .cell_deps(get_cell_deps_by_contracts(vec![Contract::AlwaysSuccess])) + .input( + CellInput::new_builder() + .previous_output(out_point.clone()) + .build(), + ) + .output( + CellOutput::new_builder() + .capacity(capacity.pack()) + .lock(get_script_by_contract( + Contract::FundingLock, + &b"whatever2"[..], + )) + .build(), + ) + .output_data(Default::default()) + .build(); + assert_eq!(submit_tx(actor, tx).await, Status::Committed); + } + + #[tokio::test] + async fn test_submit_malformed_commitment_tx() { + let actor = create_mock_chain_actor().await; + let capacity = 100u64; + let output = CellOutput::new_builder() + .capacity(capacity.pack()) + .lock(get_script_by_contract( + Contract::FundingLock, + &b"whatever1"[..], + )) + .build(); + let tx = TransactionView::new_advanced_builder() + .output(output) + .output_data(Default::default()) + .build(); + assert_eq!( + submit_tx(actor.clone(), tx.clone()).await, + Status::Committed + ); + let out_point = tx.output_pts_iter().next().unwrap(); + let tx = TransactionView::new_advanced_builder() + .cell_deps(get_cell_deps_by_contracts(vec![Contract::FundingLock])) + .input( + CellInput::new_builder() + .previous_output(out_point.clone()) + .build(), + ) + .output( + CellOutput::new_builder() + .capacity(capacity.pack()) + .lock(get_script_by_contract( + Contract::CommitmentLock, + &b"whatever2"[..], + )) + .build(), + ) + .output_data(Default::default()) + .build(); + assert_eq!(submit_tx(actor, tx).await, Status::Rejected); + } } diff --git a/src/ckb_chain/mod.rs b/src/ckb_chain/mod.rs index 5e8df5d3..13c9b1cf 100644 --- a/src/ckb_chain/mod.rs +++ b/src/ckb_chain/mod.rs @@ -9,5 +9,5 @@ pub use error::{CkbChainError, FundingError}; pub use funding::{FundingRequest, FundingTx, FundingUdtInfo}; #[cfg(test)] -pub use actor::MockChainActor; +pub use actor::{submit_tx, MockChainActor}; pub mod contracts; From 5d1bb6e9453124841e9679703a42b6d40d4d0c51 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 05:22:37 +0800 Subject: [PATCH 29/30] Add test mock context determinisim --- src/ckb_chain/contracts.rs | 52 +++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/ckb_chain/contracts.rs b/src/ckb_chain/contracts.rs index 42e29dc7..2d315479 100644 --- a/src/ckb_chain/contracts.rs +++ b/src/ckb_chain/contracts.rs @@ -213,13 +213,20 @@ fn get_environment_variable( ) } +#[cfg(test)] +impl From for ContractsContext { + fn from(mock: MockContext) -> Self { + Self::Mock(mock) + } +} + impl ContractsContext { pub fn new(network: CkbNetwork) -> Self { match network { #[cfg(test)] CkbNetwork::Mocknet => { log::warn!("Initializing mock context for testing."); - Self::Mock(MockContext::new()) + MockContext::new().into() } CkbNetwork::Dev => { let mut map = HashMap::new(); @@ -424,3 +431,46 @@ pub fn get_script_by_contract(contract: Contract, args: &[u8]) -> Script { pub fn get_cell_deps_by_contracts(contracts: Vec) -> CellDepVec { init_contracts_context(None).get_cell_deps(contracts) } + +#[cfg(test)] +mod test { + use ckb_types::{core::TransactionView, packed::CellOutput, prelude::Pack}; + use molecule::prelude::{Builder, Entity}; + + use crate::ckb_chain::contracts::{ + get_script_by_contract, Contract, ContractsContext, MockContext, + }; + + // This test is to ensure that the same transaction is generated for different mock contexts. + // If different transactions are generated, then the mock context is not deterministic. + // We can't use the mock context to verify the validity of the transactions. + #[test] + fn test_same_tx_for_different_mock_context() { + let mock_ctx1 = MockContext::new(); + let mock_ctx2 = MockContext::new(); + let capacity = 100u64; + let output = CellOutput::new_builder() + .capacity(capacity.pack()) + .lock(get_script_by_contract( + Contract::FundingLock, + &b"whatever"[..], + )) + .build(); + assert_eq!( + TransactionView::new_advanced_builder() + .cell_deps( + ContractsContext::from(mock_ctx1).get_cell_deps(vec![Contract::FundingLock]) + ) + .output(output.clone()) + .output_data(Default::default()) + .build(), + TransactionView::new_advanced_builder() + .cell_deps( + ContractsContext::from(mock_ctx2).get_cell_deps(vec![Contract::FundingLock]) + ) + .output(output.clone()) + .output_data(Default::default()) + .build() + ); + } +} From fdd6af773e158916928198dafa4a7520f0a5c738 Mon Sep 17 00:00:00 2001 From: YI Date: Wed, 5 Jun 2024 05:33:02 +0800 Subject: [PATCH 30/30] Remove outdated code --- src/ckb_chain/actor.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/ckb_chain/actor.rs b/src/ckb_chain/actor.rs index d2d41e8f..b8902405 100644 --- a/src/ckb_chain/actor.rs +++ b/src/ckb_chain/actor.rs @@ -30,17 +30,6 @@ pub struct TraceTxRequest { pub confirmations: u64, } -#[derive(Debug, Clone)] -pub struct GetScriptRequest { - pub contract: super::contracts::Contract, - pub args: Vec, -} - -#[derive(Debug, Clone)] -pub struct GetCellDepsRequest { - pub contracts: Vec, -} - #[derive(Debug)] pub enum CkbChainMessage { Fund(