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: deploy SPV contracts with type ID #6

Merged
merged 1 commit into from
Mar 25, 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
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