Skip to content

Commit

Permalink
funding: manage funding tx in an actor
Browse files Browse the repository at this point in the history
  • Loading branch information
doitian committed May 11, 2024
1 parent 6a4fcc8 commit 1ce97ce
Show file tree
Hide file tree
Showing 12 changed files with 921 additions and 18 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ serde = { version = "1.0.197", features = ["derive"] }
serde_yaml = "0.9.32"
lightning = { version = "0.0.121", features = ["max_level_trace"] }
lightning-block-sync = { version = "0.0.121", features = [
"rpc-client",
"tokio",
"rpc-client",
"tokio",
] }
lightning-invoice = { version = "0.29.0" }
lightning-net-tokio = { version = "0.0.121" }
Expand All @@ -25,19 +25,18 @@ bitcoin = { version = "0.30.2", features = ["serde"] }
bitcoin-bech32 = "0.12"
bech32 = "0.8"
libc = "0.2"

chrono = { version = "0.4", default-features = false, features = ["clock"] }
rand = "0.8.5"
serde_json = { version = "1.0" }
tokio = { version = "1", features = [
"io-util",
"macros",
"rt",
"rt-multi-thread",
"sync",
"net",
"time",
"signal",
"io-util",
"macros",
"rt",
"rt-multi-thread",
"sync",
"net",
"time",
"signal",
] }
home = "0.5.9"
ckb-sdk = "3.1.0"
Expand All @@ -51,16 +50,18 @@ once_cell = "1.19.0"
tokio-util = { version = "0.7.10", features = ["rt"] }
molecule = { version = "0.7.5", default-features = false }
ckb-types = "0.114.0"
ckb-jsonrpc-types = "0.114.0"
ckb-crypto = "0.114.0"
serde_with = { version = "3.7.0", features = ["macros", "base64"] }
hex = "0.4.3"
tower = "0.4.13"
axum = "0.7.5"
bitflags = "2.5.0"
ckb-hash = "0.115.0"
secp256k1 = { version = "0.28.0", features = ["serde"] }
secp256k1 = { version = "0.28.0", features = ["serde", "recovery"] }
musig2 = { version = "0.0.11", features = ["secp256k1", "serde"] }
ractor = "0.9.7"
linked_hash_set = "0.1.4"

[profile.release]
panic = "abort"
Expand All @@ -70,4 +71,3 @@ panic = "abort"

[dev-dependencies]
tempfile = "3.10.1"

201 changes: 201 additions & 0 deletions examples/funding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/// # How to Use This Example
///
/// Start CKB dev net first. Set the test account as the miner.
///
///
/// ```text
/// ckb init -c dev --force --ba-arg 0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7
/// ```
///
/// Transfer some CKB to the address
///
/// ```text
/// ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgx5lf4pczpamsfam48evs0c8nvwqqa59qapt46f`
/// ```
///
/// Run the example:
///
/// ```text
/// cargo run --example funding /tmp/ckb-local /tmp/ckb-remote
/// ```
use ckb_pcn_node::{
ckb::types::Hash256,
ckb_chain::{
CkbChainActor, CkbChainConfig, CkbChainMessage, FundingRequest, FundingTx, TraceTxRequest,
},
};
use ckb_types::packed;
use ractor::{call_t, cast, Actor, ActorRef};
use std::{env, path::PathBuf};

#[tokio::main]
pub async fn main() {
env_logger::init();

let (local_config, remote_config) = prepare();

let (local_actor, local_handle) = Actor::spawn(
Some("local actor".to_string()),
CkbChainActor {},
local_config,
)
.await
.expect("start local actor");

let (remote_actor, remote_handle) = Actor::spawn(
Some("remote actor".to_string()),
CkbChainActor {},
remote_config,
)
.await
.expect("start remote actor");

run(&local_actor, &remote_actor).await;

local_actor.stop(None);
local_handle.await.expect("Actor failed to exit cleanly");
remote_actor.stop(None);
remote_handle.await.expect("Actor failed to exit cleanly");
}

fn prepare() -> (CkbChainConfig, CkbChainConfig) {
let args: Vec<String> = env::args().collect();
let local_path = PathBuf::from(&args[1]);
let remote_path = PathBuf::from(&args[2]);
let _ = std::fs::create_dir_all(&local_path);
let _ = std::fs::create_dir_all(&remote_path);

// Dev net test account that has 20 billions of CKB tokens in the genesis block.
std::fs::write(
local_path.join("key"),
"d00c06bfd800d27397002dca6fb0993d5ba6399b4238b2f29ee9deb97593d2bc",
)
.expect("Unable to write to file");
// privkey for ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgx5lf4pczpamsfam48evs0c8nvwqqa59qapt46f
std::fs::write(
remote_path.join("key"),
"cccd5f7e693b60447623fb71a5983f15a426938c33699b1a81d1239cfa656cd1",
)
.expect("Unable to write to file");

let rpc_url = "http://127.0.0.1:8114".to_string();
return (
CkbChainConfig {
base_dir: Some(local_path),
rpc_url: rpc_url.clone(),
funding_source_lock_script_code_hash: make_h256(
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
),
funding_source_lock_script_hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
funding_cell_lock_script_code_hash: make_h256(
"0x8090ce20be9976e2407511502acebf74ac1cfed10d7b35b7f33f56c9bd0daec6",
),
funding_cell_lock_script_hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
},
CkbChainConfig {
base_dir: Some(remote_path),
rpc_url: rpc_url.clone(),
funding_source_lock_script_code_hash: make_h256(
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
),
funding_source_lock_script_hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
funding_cell_lock_script_code_hash: make_h256(
"0x8090ce20be9976e2407511502acebf74ac1cfed10d7b35b7f33f56c9bd0daec6",
),
funding_cell_lock_script_hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
},
);
}

const TIMEOUT_MS: u64 = 60000;

async fn run(local: &ActorRef<CkbChainMessage>, remote: &ActorRef<CkbChainMessage>) {
let mut tx = FundingTx::new();
let funding_cell_lock_script_args = packed::Bytes::default();

// The only party to fund goes first. Or the one who does not pay fee goes first for dual funding.
tx = call_t!(
local,
CkbChainMessage::Fund,
TIMEOUT_MS,
tx,
FundingRequest {
udt_info: None,
funding_cell_lock_script_args: funding_cell_lock_script_args.clone(),
local_amount: 12000000000,
local_fee_rate: 0,
remote_amount: 10000000000,
}
)
.expect("local calls")
.expect("local funds");
log_tx("local funded", &tx);

// The one who pays fee goes next.
tx = call_t!(
remote,
CkbChainMessage::Fund,
TIMEOUT_MS,
tx,
FundingRequest {
udt_info: None,
funding_cell_lock_script_args: funding_cell_lock_script_args.clone(),
local_amount: 10000000000,
local_fee_rate: 1000,
remote_amount: 12000000000,
}
)
.expect("remote calls")
.expect("remote funds");
log_tx("remote funded", &tx);

// Now tx is ready and the funding cell out point is available. Exchange and confirm commitment tx.

// Exchange funding tx signatures once commitment txs are confirmed.
tx = call_t!(local, CkbChainMessage::Sign, TIMEOUT_MS, tx)
.expect("local calls")
.expect("local signs");
log_tx("local signed", &tx);
tx = call_t!(remote, CkbChainMessage::Sign, TIMEOUT_MS, tx)
.expect("remote calls")
.expect("remote signs");
log_tx("remote signed", &tx);

let tx = tx.take().expect("take tx");
// Broadcast funding tx and waiting for confs
cast!(local, CkbChainMessage::SendTx(tx.clone())).expect("local sends");
log::info!("tx sent: {}", tx.hash());

let status = call_t!(
local,
CkbChainMessage::TraceTx,
TIMEOUT_MS,
TraceTxRequest {
tx_hash: tx.hash(),
confirmations: 3
}
)
.expect("remote calls");

log::info!("tx status of {}: {:?}", tx.hash(), status);
}

fn make_h256(hex: &str) -> Hash256 {
let mut buf = [0u8; 32];
hex::decode_to_slice(&hex[2..], &mut buf).expect("decode hash256");
Hash256::from(buf)
}

fn log_tx(name: &str, tx: &FundingTx) {
match tx.as_ref() {
Some(tx) => {
let tx: ckb_jsonrpc_types::TransactionView = tx.clone().into();
log::info!(
"tx[{}]: {}",
name,
serde_json::to_string_pretty(&tx).expect("serde json")
)
}
None => log::info!("tx[{}]: None", name),
}
}
Loading

0 comments on commit 1ce97ce

Please sign in to comment.