Skip to content

Commit

Permalink
feat: deploy SPV contracts with type ID
Browse files Browse the repository at this point in the history
  • Loading branch information
yangby-cryptape committed Mar 23, 2024
1 parent 1871f11 commit 6e6a2ed
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 37 deletions.
64 changes: 42 additions & 22 deletions src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use ckb_jsonrpc_types::TransactionView;
use ckb_sdk::{
constants::TYPE_ID_CODE_HASH,
transaction::{
builder::{CkbTransactionBuilder, SimpleTransactionBuilder},
input::InputIterator,
Expand All @@ -11,7 +12,7 @@ use ckb_sdk::{
types::{
Address as CkbAddress, AddressPayload as CkbAddressPayload, HumanCapacity, NetworkInfo,
},
SECP256K1,
ScriptId, SECP256K1,
};
use ckb_types::{bytes::Bytes, core::Capacity, packed, prelude::*};
use clap::Parser;
Expand All @@ -31,25 +32,17 @@ pub struct Args {
#[clap(flatten)]
pub(crate) ckb: super::CkbArgs,

/// A binary file, which should contain the Bitcoin SPV contract.
///
/// The repository of the contract source code is
/// <https://github.com/ckb-cell/ckb-bitcoin-spv-contracts>.
///
/// ### Warnings
///
/// Under the development phase, the compatibility has chance to be broken
/// without any declaration.
///
/// Please always use the latest versions of both the service and the contract.
///
/// TODO Matched versions of the contracts should be list.
/// A binary file, which should contain a contract that users want to deploy.
#[arg(
long = "contract-file", value_name = "CONTRACT_FILE", required = true,
value_parser = value_parsers::BinaryFileValueParser
)]
pub(crate) contract_data: Bytes,

/// Enable the type ID when deploy the contract.
#[arg(long)]
pub(crate) enable_type_id: bool,

/// The contract owner's address.
#[arg(long="contract-owner", value_parser = value_parsers::AddressValueParser)]
pub(crate) contract_owner: CkbAddress,
Expand All @@ -60,7 +53,6 @@ pub struct Args {
}

impl Args {
// TODO Deploy the Bitcoin SPV contract as type script.
pub fn execute(&self) -> Result<()> {
log::info!("Try to deploy a contract on CKB");

Expand All @@ -86,13 +78,19 @@ impl Args {
tmp
};

let output = packed::CellOutput::new_builder()
.lock((&self.contract_owner).into())
.build_exact_capacity(contract_data_capacity)
.map_err(|err| {
let msg = format!("failed to calculate the capacity for the output since {err}");
Error::other(msg)
})?;
let output_builder = packed::CellOutput::new_builder().lock((&self.contract_owner).into());

let output = if self.enable_type_id {
let type_script = ScriptId::new_type(TYPE_ID_CODE_HASH.clone()).dummy_type_id_script();
output_builder.type_(Some(type_script).pack())
} else {
output_builder
}
.build_exact_capacity(contract_data_capacity)
.map_err(|err| {
let msg = format!("failed to calculate the capacity for the output since {err}");
Error::other(msg)
})?;

let (deployer, deployer_key) = SecretKey::from_slice(&self.common.private_key.as_ref()[..])
.map(|sk| {
Expand All @@ -117,6 +115,28 @@ impl Args {
)?;

let tx_json = TransactionView::from(tx_with_groups.get_tx_view().clone());

if self.enable_type_id {
let type_script: packed::Script = tx_json
.inner
.outputs
.first()
.ok_or_else(|| {
let msg = "at least one output should be existed";
Error::other(msg)
})?
.type_
.as_ref()
.ok_or_else(|| {
let msg = "the final output must contain a type script";
Error::other(msg)
})?
.to_owned()
.into();
let type_hash = type_script.calc_script_hash();
log::info!("The contract type hash is {type_hash:#x}");
}

self.ckb
.client()
.send_transaction_ext(tx_json, self.dry_run)?;
Expand Down
47 changes: 38 additions & 9 deletions src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use ckb_types::{
prelude::*,
H256,
};
use clap::Parser;
use clap::{Args as ClapArgs, Parser};
use secp256k1::SecretKey;

use crate::{
Expand Down Expand Up @@ -68,9 +68,8 @@ pub struct Args {
#[arg(long, required = true)]
pub(crate) spv_clients_count: u8,

/// The data hash of the Bitcoin SPV contract.
#[arg(long, value_parser = value_parsers::H256ValueParser)]
pub(crate) spv_contract_data_hash: H256,
#[clap(flatten)]
pub(crate) spv_contract_code_hash: CodeHash,

/// The out point of the Bitcoin SPV contract.
#[arg(long, value_parser = value_parsers::OutPointValueParser)]
Expand Down Expand Up @@ -102,6 +101,18 @@ pub struct Args {
pub(crate) dry_run: bool,
}

#[derive(ClapArgs)]
#[group(required = true, multiple = false)]
pub struct CodeHash {
/// The data hash of the Bitcoin SPV contract.
#[arg(long, value_parser = value_parsers::H256ValueParser)]
pub(crate) spv_contract_data_hash: Option<H256>,

/// The type hash of the Bitcoin SPV contract.
#[arg(long, value_parser = value_parsers::H256ValueParser)]
pub(crate) spv_contract_type_hash: Option<H256>,
}

impl Args {
// TODO Split this method into several smaller methods.
pub fn execute(&self) -> Result<()> {
Expand Down Expand Up @@ -187,11 +198,29 @@ impl Args {
.clients_count(self.spv_clients_count.into())
.flags(flags.into())
.build();
Script::new_builder()
.code_hash(self.spv_contract_data_hash.pack())
.hash_type(ScriptHashType::Data1.into())
.args(Pack::pack(&args.as_bytes()))
.build()
match self.spv_contract_code_hash {
CodeHash {
spv_contract_data_hash: Some(ref data_hash),
spv_contract_type_hash: None,
} => Script::new_builder()
.code_hash(data_hash.pack())
.hash_type(ScriptHashType::Data1.into())
.args(Pack::pack(&args.as_bytes()))
.build(),
CodeHash {
spv_contract_data_hash: None,
spv_contract_type_hash: Some(ref type_hash),
} => Script::new_builder()
.code_hash(type_hash.pack())
.hash_type(ScriptHashType::Type.into())
.args(Pack::pack(&args.as_bytes()))
.build(),
_ => {
let msg = "only one of data hash and type hash for SPV contract \
should be input, and at least one should be input";
return Err(Error::other(msg));
}
}
};

storage.save_cells_state(
Expand Down
4 changes: 2 additions & 2 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ pub struct Cli {
pub enum Commands {
/// Deploy a contract on CKB.
///
/// This command is used to deploy the Bitcoin SPV contract.
/// Also, users can deploy the contract in their own way.
/// 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.
Init(init::Args),
Expand Down
2 changes: 1 addition & 1 deletion src/components/ckb_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl CkbRpcClientExtension for CkbRpcClient {

if !dry_run {
let tx_hash = self.send_transaction(tx_json.inner, None)?;
log::info!("Transaction hash: {tx_hash:#x}.");
log::info!("Transaction hash: {tx_hash:#x}");
println!("Send transaction: {tx_hash:#x}");
}

Expand Down
6 changes: 3 additions & 3 deletions src/components/spv_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl SpvService {
Error::other(msg)
})?
.to_owned();
log::trace!("[onchain] tip SPV client {}", spv_client_curr.client);
log::info!("[onchain] tip SPV client {}", spv_client_curr.client);

let spv_header_root_curr = &spv_client_curr.client.headers_mmr_root;
let spv_height_curr = spv_header_root_curr.max_height;
Expand Down Expand Up @@ -259,7 +259,7 @@ impl SpvService {
return Ok(true);
}

log::info!("Try to find the height when fork happened.");
log::info!("Try to find the height when fork happened");
let (stg_base_height, _) = spv.storage.base_state()?;
let mut fork_point = None;

Expand All @@ -271,7 +271,7 @@ impl SpvService {
log::debug!("[bitcoin] header#{height:07}, {btc_hash:#x}");

if stg_hash == btc_hash {
log::info!("Fork happened at height {height}.");
log::info!("Fork happened at height {height}");
fork_point = Some((height, btc_hash));
}
}
Expand Down

0 comments on commit 6e6a2ed

Please sign in to comment.