Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add subcommand to restore local storage from on-chain data #7

Merged
merged 1 commit into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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