Skip to content

Commit

Permalink
Merge pull request #7 from ckb-cell/feature/sync-onchain-data
Browse files Browse the repository at this point in the history
feat: add subcommand to restore local storage from on-chain data
  • Loading branch information
Flouse authored Mar 29, 2024
2 parents 7cec5ff + 8e72248 commit c2b4ca8
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 161 deletions.
2 changes: 1 addition & 1 deletion src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl Args {
Error::other(msg)
})?;

let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..])
let (deployer, deployer_key) = SecretKey::from_slice(&self.ckb.private_key.as_ref()[..])
.map(|sk| {
let pk = sk.public_key(&SECP256K1);
let payload = CkbAddressPayload::from_pubkey(&pk);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl Args {
tmp
};

let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..])
let (deployer, deployer_key) = SecretKey::from_slice(&self.ckb.private_key.as_ref()[..])
.map(|sk| {
let pk = sk.public_key(&SECP256K1);
let payload = CkbAddressPayload::from_pubkey(&pk);
Expand Down
40 changes: 33 additions & 7 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
mod deploy;
mod init;
mod serve;
mod sync;

#[derive(Parser)]
#[command(author, version, about)]
Expand All @@ -30,16 +31,37 @@ pub enum Commands {
/// This command can be used to deploy any contract and;
/// also, users can deploy the contract in their own way.
Deploy(deploy::Args),
/// Initialize a Bitcoin SPV instance on CKB.
/// Initialize a new Bitcoin SPV instance on CKB, and initialize local storage.
Init(init::Args),
/// Run a service to sync bitcoin headers to CKB.
/// Run a service to update a Bitcoin SPV instance base on local storage.
Serve(serve::Args),
/// Sync data to rebuild local storage base on an existed Bitcoin SPV instance.
Sync(sync::Args),
}

#[derive(Parser)]
pub struct CommonArgs {
#[command(flatten)]
pub(crate) verbose: Verbosity<InfoLevel>,
}

#[derive(Parser)]
pub struct CkbArgs {
/// CKB JSON-RPC APIs endpoint.
#[arg(long)]
pub(crate) ckb_endpoint: Url,

/// The network type of the CKB chain which connected.
#[arg(
long = "network-type",
value_parser = value_parsers::NetworkTypeValueParser,
default_value = "testnet"
)]
pub network: NetworkType,

/// The fee rate for CKB transactions.
#[arg(long = "ckb-fee-rate", default_value = "1000")]
pub(crate) fee_rate: u64,

/// A binary file, which contains a secp256k1 private key.
/// This private key will be used to provide all CKBytes.
Expand All @@ -50,7 +72,7 @@ pub struct CommonArgs {
}

#[derive(Parser)]
pub struct CkbArgs {
pub struct CkbRoArgs {
/// CKB JSON-RPC APIs endpoint.
#[arg(long)]
pub(crate) ckb_endpoint: Url,
Expand All @@ -62,10 +84,6 @@ pub struct CkbArgs {
default_value = "testnet"
)]
pub network: NetworkType,

/// The fee rate for CKB transactions.
#[arg(long = "ckb-fee-rate", default_value = "1000")]
pub(crate) fee_rate: u64,
}

#[derive(Parser)]
Expand Down Expand Up @@ -103,6 +121,7 @@ impl Cli {
Commands::Deploy(args) => args.execute()?,
Commands::Init(args) => args.execute()?,
Commands::Serve(args) => args.execute()?,
Commands::Sync(args) => args.execute()?,
}
log::info!("Bitcoin SPV on CKB service is stopped.");
Ok(())
Expand All @@ -113,6 +132,7 @@ impl Cli {
Commands::Deploy(ref args) => args.common.configure_logger(),
Commands::Init(ref args) => args.common.configure_logger(),
Commands::Serve(ref args) => args.common.configure_logger(),
Commands::Sync(ref args) => args.common.configure_logger(),
}
}
}
Expand All @@ -131,6 +151,12 @@ impl CkbArgs {
}
}

impl CkbRoArgs {
pub fn client(&self) -> CkbRpcClient {
CkbRpcClient::new(self.ckb_endpoint.as_str())
}
}

impl BitcoinArgs {
pub fn client(&self) -> BitcoinClient {
BitcoinClient::new(
Expand Down
4 changes: 2 additions & 2 deletions src/cli/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl Args {
tmp
};

let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..])
let (deployer, deployer_key) = SecretKey::from_slice(&self.ckb.private_key.as_ref()[..])
.map(|sk| {
let pk = sk.public_key(&SECP256K1);
let payload = CkbAddressPayload::from_pubkey(&pk);
Expand Down Expand Up @@ -354,7 +354,7 @@ impl Args {
tmp
};

let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..])
let (deployer, deployer_key) = SecretKey::from_slice(&self.ckb.private_key.as_ref()[..])
.map(|sk| {
let pk = sk.public_key(&SECP256K1);
let payload = CkbAddressPayload::from_pubkey(&pk);
Expand Down
117 changes: 117 additions & 0 deletions src/cli/sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! The `sync` sub-command.
use std::path::PathBuf;

use ckb_sdk::rpc::ResponseFormatGetter as _;
use ckb_types::{
core::DepType,
packed::{CellDep, CellOutput, OutPoint},
prelude::*,
};
use clap::Parser;

use crate::{
components::Storage,
prelude::*,
result::{Error, Result},
utilities::value_parsers,
};

#[derive(Parser)]
pub struct Args {
#[clap(flatten)]
pub(crate) common: super::CommonArgs,

/// The directory, which stores all cached data.
#[arg(long)]
pub(crate) data_dir: PathBuf,

#[clap(flatten)]
pub(crate) ckb: super::CkbRoArgs,

#[clap(flatten)]
pub(crate) bitcoin: super::BitcoinArgs,

/// The out point of the Bitcoin SPV contract.
#[arg(long, value_parser = value_parsers::OutPointValueParser)]
pub(crate) spv_contract_out_point: OutPoint,

/// The out point of the lock contract.
///
/// The lock contract has to satisfy that:
/// - If total capacity of cells which use this lock script were not
/// decreased, any non-owner users can update them.
#[arg(long, value_parser = value_parsers::OutPointValueParser)]
pub(crate) lock_contract_out_point: OutPoint,

/// An out point of any cell in the target Bitcoin SPV instance.
#[arg(long, value_parser = value_parsers::OutPointValueParser)]
pub(crate) spv_cell_out_point: OutPoint,
}

impl Args {
pub fn execute(&self) -> Result<()> {
log::info!("Sync data to local storage base on on-chain Bitcoin SPV instance");

let ckb_cli = self.ckb.client();

let input_tx_hash = self.spv_cell_out_point.tx_hash();
let input_cell_index: u32 = self.spv_cell_out_point.index().unpack();
let input_cell_output: CellOutput = ckb_cli
.get_transaction(input_tx_hash.unpack())?
.ok_or_else(|| {
let msg = format!("CKB transaction {input_tx_hash:#x} is not existed");
Error::other(msg)
})?
.transaction
.ok_or_else(|| {
let msg = format!("remote server replied empty for transaction {input_tx_hash:#x}");
Error::other(msg)
})?
.get_value()?
.inner
.outputs
.get(input_cell_index as usize)
.ok_or_else(|| {
let msg = format!(
"CKB transaction {input_tx_hash:#x} doesn't have \
the {input_cell_index}-th output"
);
Error::other(msg)
})?
.to_owned()
.into();
let spv_type_script = input_cell_output.type_().to_opt().ok_or_else(|| {
let msg = format!(
"input cell (tx-hash: {input_tx_hash:#x}, index: {input_cell_index}) \
is not a SPV cell since no type script"
);
Error::other(msg)
})?;

let tip_spv_client_cell = ckb_cli.find_best_spv_client(spv_type_script.clone(), None)?;
let start_height = tip_spv_client_cell.client.headers_mmr_root.min_height;

let btc_cli = self.bitcoin.client();
let start_header = btc_cli.get_block_header_by_height(start_height)?;

let storage = Storage::new(&self.data_dir)?;
let _ = storage.initialize_with(start_height, start_header)?;

let spv_contract_cell_dep = CellDep::new_builder()
.out_point(self.spv_contract_out_point.clone())
.dep_type(DepType::Code.into())
.build();
let lock_contract_cell_dep = CellDep::new_builder()
.out_point(self.lock_contract_out_point.clone())
.dep_type(DepType::Code.into())
.build();
storage.save_cells_state(
spv_type_script,
spv_contract_cell_dep,
lock_contract_cell_dep,
)?;

Ok(())
}
}
Loading

0 comments on commit c2b4ca8

Please sign in to comment.