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: support type id script handler #75

Closed
Closed
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
1 change: 1 addition & 0 deletions src/tests/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod sighash;
pub mod typeid;
67 changes: 67 additions & 0 deletions src/tests/transaction/typeid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use ckb_types::{packed::CellOutput, prelude::*};

use crate::{
constants::{self, ONE_CKB},
tests::{build_sighash_script, init_context, ACCOUNT1_ARG, ACCOUNT1_KEY, FEE_RATE},
transaction::{
builder::{CkbTransactionBuilder, SimpleTransactionBuilder},
input::InputIterator,
signer::{SignContexts, TransactionSigner},
TransactionBuilderConfiguration,
},
NetworkInfo, ScriptId,
};

#[test]
fn test_deploy_id() {
let sender = build_sighash_script(ACCOUNT1_ARG);
let ctx = init_context(Vec::new(), vec![(sender.clone(), Some(10_0000 * ONE_CKB))]);

let network_info = NetworkInfo::testnet();
let type_script = ScriptId::new_type(constants::TYPE_ID_CODE_HASH.clone()).dummy_script();

let output = CellOutput::new_builder()
.capacity((120 * ONE_CKB).pack())
.lock(sender.clone())
.type_(Some(type_script).pack())
.build();
let configuration =
TransactionBuilderConfiguration::new_with_network(network_info.clone()).unwrap();

let iterator = InputIterator::new_with_cell_collector(
vec![sender.clone()],
Box::new(ctx.to_live_cells_context()) as Box<_>,
);
let mut builder = SimpleTransactionBuilder::new(configuration, iterator);
builder.add_output(output, bytes::Bytes::from(vec![0x01u8; 64]).pack());
builder.set_change_lock(sender.clone());
let mut tx_with_groups = builder.build(&Default::default()).expect("build failed");

let json_tx = ckb_jsonrpc_types::TransactionView::from(tx_with_groups.get_tx_view().clone());
println!("tx: {}", serde_json::to_string_pretty(&json_tx).unwrap());

TransactionSigner::new(&network_info)
.sign_transaction(
&mut tx_with_groups,
&SignContexts::new_sighash_h256(vec![ACCOUNT1_KEY.clone()]).unwrap(),
)
.unwrap();

let json_tx = ckb_jsonrpc_types::TransactionView::from(tx_with_groups.get_tx_view().clone());
println!("tx: {}", serde_json::to_string_pretty(&json_tx).unwrap());

let tx = tx_with_groups.get_tx_view().clone();
let script_groups = tx_with_groups.script_groups.clone();
assert_eq!(script_groups.len(), 2);
assert_eq!(tx.header_deps().len(), 0);
assert_eq!(tx.cell_deps().len(), 1);
assert_eq!(tx.inputs().len(), 1);
for out_point in tx.input_pts_iter() {
assert_eq!(ctx.get_input(&out_point).unwrap().0.lock(), sender);
}
assert_eq!(tx.outputs().len(), 2);
// assert_eq!(tx.output(0).unwrap(), output);
assert_eq!(tx.output(1).unwrap().lock(), sender);

ctx.verify(tx, FEE_RATE).unwrap();
}
22 changes: 22 additions & 0 deletions src/transaction/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,27 @@ impl SimpleTransactionBuilder {
tx_builder.set_output(idx, output);
Ok(())
}

fn post_build(
type_groups: &HashMap<Byte32, ScriptGroup>,
configuration: &TransactionBuilderConfiguration,
tx_builder: &mut TransactionBuilder,
contexts: &HandlerContexts,
) -> Result<(), TxBuilderError> {
for idx in type_groups
.values()
.flat_map(|group| group.output_indices.iter())
{
for handler in configuration.get_script_handlers() {
for context in &contexts.contexts {
if handler.post_build(*idx, tx_builder, context.as_ref())? {
break;
}
}
}
}
Ok(())
}
}

macro_rules! celloutput_capacity {
Expand Down Expand Up @@ -269,6 +290,7 @@ impl CkbTransactionBuilder for SimpleTransactionBuilder {
if !state.is_success() {
return Err(TxBuilderError::BalanceCapacity(state.into()));
}
Self::post_build(&type_groups, &self.configuration, &mut self.tx, contexts)?;
let script_groups = lock_groups
.into_values()
.chain(type_groups.into_values())
Expand Down
15 changes: 14 additions & 1 deletion src/transaction/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use self::sighash::Secp256k1Blake160SighashAllScriptContext;

pub mod multisig;
pub mod sighash;
pub mod typeid;

pub trait ScriptHandler {
/// Try to build transaction with the given script_group and context.
Expand All @@ -21,6 +22,15 @@ pub trait ScriptHandler {
context: &dyn HandlerContext,
) -> Result<bool, TxBuilderError>;

fn post_build(
&self,
_index: usize,
_tx_builder: &mut TransactionBuilder,
_context: &dyn HandlerContext,
) -> Result<bool, TxBuilderError> {
Ok(false)
}

fn init(&mut self, network: &NetworkInfo) -> Result<(), TxBuilderError>;
}

Expand All @@ -43,7 +53,10 @@ pub struct HandlerContexts {
impl Default for HandlerContexts {
fn default() -> Self {
Self {
contexts: vec![Box::new(Secp256k1Blake160SighashAllScriptContext {})],
contexts: vec![
Box::new(Secp256k1Blake160SighashAllScriptContext {}),
Box::new(typeid::TypeIdContext {}),
],
}
}
}
Expand Down
104 changes: 104 additions & 0 deletions src/transaction/handler/typeid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use ckb_hash::new_blake2b;
use ckb_types::{
packed::{CellInput, Script},
prelude::*,
};

use crate::{
core::TransactionBuilder, tx_builder::TxBuilderError, NetworkInfo, ScriptGroup,
ScriptGroupType, ScriptId,
};

use super::{HandlerContext, ScriptHandler};

pub struct TypeIdHandler;

pub struct TypeIdContext {}

impl HandlerContext for TypeIdContext {}

impl TypeIdHandler {
pub fn is_match(&self, script: &Script) -> bool {
ScriptId::from(script).is_type_id()
}
}

// copy from https://github.com/nervosnetwork/ckb-cli/blob/develop/src/utils/other.rs#L325
pub fn calculate_type_id(first_cell_input: &CellInput, output_index: u64) -> [u8; 32] {
let mut blake2b = new_blake2b();
blake2b.update(first_cell_input.as_slice());
blake2b.update(&output_index.to_le_bytes());
let mut ret = [0u8; 32];
blake2b.finalize(&mut ret);
ret
}

impl ScriptHandler for TypeIdHandler {
fn build_transaction(
&self,
tx_builder: &mut TransactionBuilder,
script_group: &ScriptGroup,
context: &dyn HandlerContext,
) -> Result<bool, TxBuilderError> {
if script_group.group_type != ScriptGroupType::Type
|| !self.is_match(&script_group.script)
|| script_group.output_indices.is_empty()
{
return Ok(false);
}
if let Some(_args) = context.as_any().downcast_ref::<TypeIdContext>() {
let index = script_group.output_indices.last().unwrap();
let output = tx_builder.get_outputs()[*index].clone();
if let Some(type_) = output.type_().to_opt() {
if self.is_match(&type_) && type_.args().is_empty() {
let type_ = type_
.as_builder()
.args(bytes::Bytes::from(vec![0u8; 32]).pack())
.build();
let output = output.as_builder().type_(Some(type_).pack()).build();
tx_builder.set_output(*index, output);
}

return Ok(true);
}
}
Ok(false)
}

fn init(&mut self, _network: &NetworkInfo) -> Result<(), TxBuilderError> {
Ok(())
}

fn post_build(
&self,
index: usize,
tx_builder: &mut TransactionBuilder,
_context: &dyn HandlerContext,
) -> Result<bool, TxBuilderError> {
if let Some(output) = tx_builder.get_outputs().get(index) {
if let Some(type_) = output.type_().to_opt() {
if self.is_match(&type_) {
if type_.args().raw_data()[..] == [0u8; 32] {
let input = tx_builder
.get_inputs()
.get(0)
.ok_or(TxBuilderError::InvalidInputIndex(0))?;
let args = calculate_type_id(input, index as u64);
let type_ = type_
.as_builder()
.args(bytes::Bytes::from(args.to_vec()).pack())
.build();
let output = output
.clone()
.as_builder()
.type_(Some(type_).pack())
.build();
tx_builder.set_output(index, output);
}
return Ok(true);
}
}
}
Ok(false)
}
}
1 change: 1 addition & 0 deletions src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ impl TransactionBuilderConfiguration {
network,
)?,
) as Box<_>,
Box::new(handler::typeid::TypeIdHandler) as Box<_>,
];
Ok(ret)
}
Expand Down
3 changes: 3 additions & 0 deletions src/tx_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ pub enum TxBuilderError {
#[error("can not find specifed output to put small change")]
NoOutputForSmallChange,

#[error("invalid input index: `{0}`")]
InvalidInputIndex(usize),

#[error("other error: `{0}`")]
Other(anyhow::Error),
}
Expand Down