From dae479a751fd2c3a4991da11abd5c2076e45b735 Mon Sep 17 00:00:00 2001 From: Liu Chuankai Date: Thu, 11 May 2023 14:46:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20support=20dao=20withdraw?= =?UTF-8?q?=20phrase2=20simple=20transaction=20builder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/dao_deposit_example.rs | 2 +- examples/dao_withdraw_phrase1_example.rs | 2 +- examples/dao_withdraw_phrase2_example.rs | 61 +++++++ examples/send_ckb_example.rs | 2 +- examples/send_ckb_multisig_example.rs | 2 +- src/core/advanced_builders.rs | 49 +++++- src/tests/transaction/sighash.rs | 12 +- src/transaction/builder/mod.rs | 40 +++-- src/transaction/handler/dao.rs | 198 ++++++++++++++++++----- src/transaction/handler/mod.rs | 12 +- src/transaction/handler/multisig.rs | 4 +- src/transaction/handler/sighash.rs | 7 +- src/unlock/signer.rs | 10 +- 13 files changed, 321 insertions(+), 80 deletions(-) create mode 100644 examples/dao_withdraw_phrase2_example.rs diff --git a/examples/dao_deposit_example.rs b/examples/dao_deposit_example.rs index 61d04238..4f4c9e8a 100644 --- a/examples/dao_deposit_example.rs +++ b/examples/dao_deposit_example.rs @@ -26,7 +26,7 @@ fn main() -> Result<(), Box> { contexts.add_context(Box::new(context) as Box<_>); builder.set_change_lock((&sender).into()); - let mut tx_with_groups = builder.build(&contexts)?; + let mut tx_with_groups = builder.build(&mut contexts)?; 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()); diff --git a/examples/dao_withdraw_phrase1_example.rs b/examples/dao_withdraw_phrase1_example.rs index 9de12a70..5698c1d5 100644 --- a/examples/dao_withdraw_phrase1_example.rs +++ b/examples/dao_withdraw_phrase1_example.rs @@ -35,7 +35,7 @@ fn main() -> Result<(), Box> { contexts.add_context(Box::new(context) as Box<_>); builder.set_change_lock((&sender).into()); - let mut tx_with_groups = builder.build(&contexts)?; + let mut tx_with_groups = builder.build(&mut contexts)?; 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()); diff --git a/examples/dao_withdraw_phrase2_example.rs b/examples/dao_withdraw_phrase2_example.rs new file mode 100644 index 00000000..e29239b7 --- /dev/null +++ b/examples/dao_withdraw_phrase2_example.rs @@ -0,0 +1,61 @@ +use ckb_sdk::{ + transaction::{ + builder::{CkbTransactionBuilder, SimpleTransactionBuilder}, + handler::{dao, HandlerContexts}, + input::InputIterator, + signer::{SignContexts, TransactionSigner}, + TransactionBuilderConfiguration, + }, + Address, CkbRpcClient, NetworkInfo, +}; +use ckb_types::h256; +use std::{error::Error as StdErr, str::FromStr}; + +fn main() -> Result<(), Box> { + let network_info = NetworkInfo::testnet(); + let sender = Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq2qf8keemy2p5uu0g0gn8cd4ju23s5269qk8rg4r")?; + + let configuration = TransactionBuilderConfiguration::new_with_network(network_info.clone())?; + + let iterator = InputIterator::new(vec![(&sender).into()], configuration.network_info()); + let mut builder = SimpleTransactionBuilder::new(configuration, iterator); + + let input_outpoint = serde_json::from_str::( + r#" + { + "tx_hash": "0x770f930ed3bf35664cb6a112edce3287712f0613c74c1f1176e099ee51268489", + "index": "0x0" + } + "#, + ) + .unwrap(); + let context = + dao::WithdrawPhrase2Context::new(vec![input_outpoint.into()], network_info.url.clone()); + let mut contexts = HandlerContexts::default(); + contexts.add_context(Box::new(context) as Box<_>); + + builder.set_change_lock((&sender).into()); + let mut tx_with_groups = builder.build(&mut contexts)?; + + 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 private_keys = vec![h256!( + "0x6c9ed03816e3111e49384b8d180174ad08e29feb1393ea1b51cef1c505d4e36a" + )]; + TransactionSigner::new(&network_info).sign_transaction( + &mut tx_with_groups, + &SignContexts::new_sighash_h256(private_keys)?, + )?; + + 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_hash = CkbRpcClient::new(network_info.url.as_str()) + .send_transaction(json_tx.inner, None) + .expect("send transaction"); + // example tx: 0xaae93c573848a632f06f01c7c444c90aa490253f35b4212d147882266960a267 + println!(">>> tx {} sent! <<<", tx_hash); + + Ok(()) +} diff --git a/examples/send_ckb_example.rs b/examples/send_ckb_example.rs index 35a88806..93dd85bc 100644 --- a/examples/send_ckb_example.rs +++ b/examples/send_ckb_example.rs @@ -28,7 +28,7 @@ fn main() -> Result<(), Box> { let addr = Address::from_str(sender)?; builder.add_output_from_addr(&receiver, Capacity::shannons(510_0000_0000u64)); builder.set_change_addr(&addr); - let mut tx_with_groups = builder.build(&Default::default())?; + let mut tx_with_groups = builder.build(&mut Default::default())?; 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()); diff --git a/examples/send_ckb_multisig_example.rs b/examples/send_ckb_multisig_example.rs index 5b45aa94..91e42ebe 100644 --- a/examples/send_ckb_multisig_example.rs +++ b/examples/send_ckb_multisig_example.rs @@ -40,7 +40,7 @@ fn main() -> Result<(), Box> { builder.add_output_from_addr(&addr, Capacity::shannons(501_0000_0000u64)); builder.set_change_addr(&sender_addr); let mut tx_with_groups = - builder.build(&HandlerContexts::new_multisig(multisig_config.clone()))?; + builder.build(&mut HandlerContexts::new_multisig(multisig_config.clone()))?; 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()); diff --git a/src/core/advanced_builders.rs b/src/core/advanced_builders.rs index 8a39d59e..4a830152 100644 --- a/src/core/advanced_builders.rs +++ b/src/core/advanced_builders.rs @@ -1,4 +1,8 @@ -use ckb_types::{constants, core, packed, prelude::*}; +use ckb_types::{ + constants, core, + packed::{self, WitnessArgs}, + prelude::*, +}; use derive_getters::Getters; /// An advanced builder for [`TransactionView`]. @@ -20,7 +24,7 @@ pub struct TransactionBuilder { #[getter(rename = "get_outputs")] pub outputs: Vec, #[getter(rename = "get_witnesses")] - pub witnesses: Vec, + pub witnesses: Vec, #[getter(rename = "get_outputs_data")] pub outputs_data: Vec, } @@ -138,11 +142,12 @@ macro_rules! def_dedup_setter_for_vector { $comment_push:expr, $comment_extend:expr, ) => { #[doc = $comment_push] - pub fn $func_push(&mut self, v: $prefix::$type) -> &mut Self { - if !self.$field.contains(&v) { - self.$field.push(v); + pub fn $func_push(&mut self, v: $prefix::$type) -> usize { + if let Some(idx) = self.$field.iter().position(|x| x == &v) { + return idx; } - self + self.$field.push(v); + self.$field.len() - 1 } #[doc = $comment_extend] pub fn $func_extend(&mut self, v: T) -> &mut Self @@ -207,7 +212,7 @@ impl TransactionBuilder { def_setter_for_vector!( set_i, witnesses, - Bytes, + WitnessArgs, witness, witnesses, set_witnesses, @@ -221,6 +226,32 @@ impl TransactionBuilder { set_outputs_data ); + pub fn set_witness_lock(&mut self, i: usize, v: Option) -> &mut Self { + self.witnesses[i] = self.witnesses[i] + .clone() + .as_builder() + .lock(v.pack()) + .build(); + self + } + + pub fn set_witness_input(&mut self, i: usize, v: Option) -> &mut Self { + self.witnesses[i] = self.witnesses[i] + .clone() + .as_builder() + .input_type(v.pack()) + .build(); + self + } + + pub fn set_witness_output(&mut self, i: usize, v: Option) -> &mut Self { + self.witnesses[i] = self.witnesses[i] + .clone() + .as_builder() + .output_type(v.pack()) + .build(); + self + } /// Converts into [`TransactionView`](struct.TransactionView.html). pub fn build(self) -> core::TransactionView { let Self { @@ -240,6 +271,10 @@ impl TransactionBuilder { .outputs(outputs.pack()) .outputs_data(outputs_data.pack()) .build(); + let witnesses = witnesses + .iter() + .map(|t| t.as_bytes().pack()) + .collect::>(); let tx = packed::Transaction::new_builder() .raw(raw) .witnesses(witnesses.pack()) diff --git a/src/tests/transaction/sighash.rs b/src/tests/transaction/sighash.rs index 162366d5..fcd012aa 100644 --- a/src/tests/transaction/sighash.rs +++ b/src/tests/transaction/sighash.rs @@ -43,7 +43,9 @@ fn test_transfer_from_sighash() { let mut builder = SimpleTransactionBuilder::new(configuration, iterator); builder.add_output(output.clone(), ckb_types::packed::Bytes::default()); builder.set_change_lock(sender.clone()); - let mut tx_with_groups = builder.build(&Default::default()).expect("build failed"); + let mut tx_with_groups = builder + .build(&mut 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()); @@ -106,7 +108,9 @@ fn test_transfer_from_sighash_samll_to_fee() { let mut builder = SimpleTransactionBuilder::new(configuration, iterator); builder.add_output(output.clone(), ckb_types::packed::Bytes::default()); builder.set_change_lock(sender.clone()); - let mut tx_with_groups = builder.build(&Default::default()).expect("build failed"); + let mut tx_with_groups = builder + .build(&mut 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()); @@ -167,7 +171,9 @@ fn test_transfer_from_sighash_samll_to_receiver() { let mut builder = SimpleTransactionBuilder::new(configuration, iterator); builder.add_output(output, ckb_types::packed::Bytes::default()); builder.set_change_lock(sender.clone()); - let mut tx_with_groups = builder.build(&Default::default()).expect("build failed"); + let mut tx_with_groups = builder + .build(&mut 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()); diff --git a/src/transaction/builder/mod.rs b/src/transaction/builder/mod.rs index f7de29be..32143e01 100644 --- a/src/transaction/builder/mod.rs +++ b/src/transaction/builder/mod.rs @@ -22,7 +22,7 @@ pub use fee_calculator::FeeCalculator; pub trait CkbTransactionBuilder { fn build( &mut self, - contexts: &HandlerContexts, + contexts: &mut HandlerContexts, ) -> Result; } @@ -36,6 +36,12 @@ pub struct SimpleTransactionBuilder { reward: u64, } +pub struct PrepareTransactionViewer<'a> { + pub(crate) transaction_inputs: &'a mut Vec, + pub(crate) tx: &'a mut TransactionBuilder, + pub(crate) reward: &'a mut u64, +} + impl SimpleTransactionBuilder { pub fn new(configuration: TransactionBuilderConfiguration, input_iter: InputIterator) -> Self { Self { @@ -137,16 +143,13 @@ impl SimpleTransactionBuilder { } fn prepare_transaction( - transaction_inputs: &mut Vec, - tx_builder: &mut crate::core::TransactionBuilder, + viewer: &mut PrepareTransactionViewer, configuration: &TransactionBuilderConfiguration, - contexts: &HandlerContexts, + contexts: &mut HandlerContexts, ) -> Result<(), TxBuilderError> { for handler in configuration.get_script_handlers() { - for context in &contexts.contexts { - if let Ok(true) = - handler.prepare_transaction(transaction_inputs, tx_builder, context.as_ref()) - { + for context in &mut contexts.contexts { + if handler.prepare_transaction(viewer, context.as_mut())? { break; } } @@ -162,17 +165,22 @@ macro_rules! celloutput_capacity { }}; } +macro_rules! prepare_veiwer { + ($self:ident) => { + PrepareTransactionViewer { + transaction_inputs: &mut $self.transaction_inputs, + tx: &mut $self.tx, + reward: &mut $self.reward, + } + }; +} + impl CkbTransactionBuilder for SimpleTransactionBuilder { fn build( &mut self, - contexts: &HandlerContexts, + contexts: &mut HandlerContexts, ) -> Result { - Self::prepare_transaction( - &mut self.transaction_inputs, - &mut self.tx, - &self.configuration, - contexts, - )?; + Self::prepare_transaction(&mut prepare_veiwer!(self), &self.configuration, contexts)?; let mut lock_groups: HashMap = HashMap::default(); let mut type_groups: HashMap = HashMap::default(); let mut outputs_capacity = 0u64; @@ -198,7 +206,7 @@ impl CkbTransactionBuilder for SimpleTransactionBuilder { let input = input?; self.tx.input(input.cell_input()); let previous_output = input.previous_output(); - self.tx.witness(packed::Bytes::default()); + self.tx.witness(Default::default()); let lock_script = previous_output.lock(); let script_group = lock_groups .entry(lock_script.calc_script_hash()) diff --git a/src/transaction/handler/dao.rs b/src/transaction/handler/dao.rs index 1eddf418..b9ce29bd 100644 --- a/src/transaction/handler/dao.rs +++ b/src/transaction/handler/dao.rs @@ -1,32 +1,39 @@ +use std::collections::HashMap; + use anyhow::anyhow; use ckb_types::{ - core::{DepType, ScriptHashType}, + core::{Capacity, DepType, ScriptHashType}, h256, - packed::{CellDep, CellInput, CellOutput, OutPoint, Script, WitnessArgs}, - prelude::{Builder, Entity, Pack}, + packed::{CellDep, CellInput, CellOutput, OutPoint, Script}, + prelude::{Builder, Entity, Pack, Unpack}, }; use lazy_static::lazy_static; use crate::{ constants, traits::{ - DefaultHeaderDepResolver, DefaultTransactionDependencyProvider, HeaderDepResolver, LiveCell, + DefaultHeaderDepResolver, DefaultTransactionDependencyProvider, HeaderDepResolver, + LiveCell, TransactionDependencyProvider, }, - transaction::input::TransactionInput, + transaction::{builder::PrepareTransactionViewer, input::TransactionInput}, tx_builder::{ dao::{DaoDepositReceiver, DaoPrepareItem}, TxBuilderError, }, - NetworkInfo, NetworkType, ScriptGroup, + util::{calculate_dao_maximum_withdraw4, minimal_unlock_point}, + NetworkInfo, NetworkType, ScriptGroup, Since, SinceType, }; use super::{HandlerContext, ScriptHandler}; +pub const DAO_DATA_LEN: usize = 8; lazy_static! { static ref DAO_TYPE_SCRIPT: Script = Script::new_builder() .code_hash(constants::DAO_TYPE_HASH.pack()) .hash_type(ScriptHashType::Type.into()) .build(); + static ref DEPOSIT_CELL_DATA: ckb_types::packed::Bytes = + bytes::Bytes::from(vec![0u8; DAO_DATA_LEN]).pack(); } pub struct DaoScriptHandler { @@ -90,6 +97,26 @@ impl WithdrawPhrase1Context { impl HandlerContext for WithdrawPhrase1Context {} +pub struct WithdrawPhrase2Context { + /// Withdraw from those out_points (prepared cells) + items: Vec, + rpc_url: String, + // input_index => deposit_header_index + deposit_header_indexes: HashMap, +} + +impl WithdrawPhrase2Context { + pub fn new(items: Vec, rpc_url: String) -> Self { + Self { + items, + rpc_url, + deposit_header_indexes: HashMap::new(), + } + } +} + +impl HandlerContext for WithdrawPhrase2Context {} + impl DaoScriptHandler { pub fn is_match(&self, script: &Script) -> bool { script.code_hash() == constants::DAO_TYPE_HASH.pack() @@ -101,8 +128,7 @@ impl DaoScriptHandler { } pub fn build_phrase1_base( - transaction_inputs: &mut Vec, - tx_data: &mut crate::core::TransactionBuilder, + viewer: &mut PrepareTransactionViewer, context: &WithdrawPhrase1Context, ) -> Result<(), TxBuilderError> { if context.items.is_empty() { @@ -145,19 +171,120 @@ impl DaoScriptHandler { tx_index: u32::MAX, // TODO set correct tx_index }; let transaction_input = TransactionInput::new(live_cell, 0); - transaction_inputs.push(transaction_input); + viewer.transaction_inputs.push(transaction_input); + + viewer.tx.dedup_header_dep(deposit_header.hash()); + + viewer.tx.output(output); + viewer.tx.output_data(output_data.pack()); + } + Ok(()) + } + + pub fn build_phrase2_base( + viewer: &mut PrepareTransactionViewer, + context: &mut WithdrawPhrase2Context, + ) -> Result<(), TxBuilderError> { + if context.items.is_empty() { + return Err(TxBuilderError::InvalidParameter(anyhow!( + "No cell to withdraw" + ))); + } + + let header_dep_resolver = DefaultHeaderDepResolver::new(&context.rpc_url); + let tx_dep_provider = DefaultTransactionDependencyProvider::new(&context.rpc_url, 10); + + let mut prepare_block_hashes = Vec::new(); + for out_point in &context.items { + let tx_hash = out_point.tx_hash(); + let prepare_header = header_dep_resolver + .resolve_by_tx(&tx_hash) + .map_err(TxBuilderError::Other)? + .ok_or_else(|| TxBuilderError::ResolveHeaderDepByTxHashFailed(tx_hash.clone()))?; + prepare_block_hashes.push(prepare_header.hash()); + let input_cell = tx_dep_provider.get_cell(out_point)?; + if input_cell.type_().to_opt().as_ref() != Some(&DAO_TYPE_SCRIPT) { + return Err(TxBuilderError::InvalidParameter(anyhow!( + "the input cell has invalid type script" + ))); + } + + let data = tx_dep_provider.get_cell_data(out_point)?; + if data.len() != DAO_DATA_LEN { + return Err(TxBuilderError::InvalidParameter(anyhow!( + "the input cell has invalid data length, expected: 8, got: {}", + data.len() + ))); + } - tx_data.dedup_header_dep(deposit_header.hash()); + let deposit_header = { + let deposit_number = { + let mut number_bytes = [0u8; DAO_DATA_LEN]; + number_bytes.copy_from_slice(data.as_ref()); + u64::from_le_bytes(number_bytes) + }; + header_dep_resolver + .resolve_by_number(deposit_number) + .or_else(|_err| { + // for light client + let prepare_tx = tx_dep_provider.get_transaction(&tx_hash)?; + for input in prepare_tx.inputs() { + let _ = header_dep_resolver + .resolve_by_tx(&input.previous_output().tx_hash())?; + } + header_dep_resolver.resolve_by_number(deposit_number) + }) + .map_err(TxBuilderError::Other)? + .ok_or(TxBuilderError::ResolveHeaderDepByNumberFailed( + deposit_number, + ))? + }; - tx_data.output(output); - tx_data.output_data(output_data.pack()); + // calculate reward + { + let occupied_capacity = input_cell + .occupied_capacity(Capacity::bytes(data.len()).unwrap()) + .unwrap(); + let input_capacity = calculate_dao_maximum_withdraw4( + &deposit_header, + &prepare_header, + &input_cell, + occupied_capacity.as_u64(), + ); + let tmp_capacity: u64 = input_cell.capacity().unpack(); + *viewer.reward += input_capacity - tmp_capacity; + } + // build live cell + { + let unlock_point = minimal_unlock_point(&deposit_header, &prepare_header); + let since = Since::new( + SinceType::EpochNumberWithFraction, + unlock_point.full_value(), + false, + ); + let live_cell = LiveCell { + output: input_cell, + output_data: data, + out_point: out_point.clone(), + block_number: deposit_header.number(), + tx_index: u32::MAX, // TODO set correct tx_index + }; + let transaction_input = TransactionInput::new(live_cell, since.value()); + viewer.transaction_inputs.push(transaction_input); + }; + let deposit_block_hash = deposit_header.hash(); + let dep_header_idx = viewer.tx.dedup_header_dep(deposit_block_hash); + context + .deposit_header_indexes + .insert(viewer.transaction_inputs.len() - 1, dep_header_idx); } + viewer.tx.dedup_header_deps(prepare_block_hashes); + Ok(()) } pub fn build_deposit( - _transaction_inputs: &mut [TransactionInput], - tx_data: &mut crate::core::TransactionBuilder, + viewer: &mut PrepareTransactionViewer, context: &DepositContext, ) -> Result<(), TxBuilderError> { if context.receivers.is_empty() { @@ -165,19 +292,14 @@ impl DaoScriptHandler { "empty dao receivers" ))); } - let dao_type_script = Script::new_builder() - .code_hash(constants::DAO_TYPE_HASH.pack()) - .hash_type(ScriptHashType::Type.into()) - .build(); - for receiver in &context.receivers { let output = CellOutput::new_builder() .capacity(receiver.capacity.pack()) .lock(receiver.lock_script.clone()) - .type_(Some(dao_type_script.clone()).pack()) + .type_(Some(DAO_TYPE_SCRIPT.clone()).pack()) .build(); - tx_data.output(output); - tx_data.output_data(bytes::Bytes::from(vec![0u8; 8]).pack()); + viewer.tx.output(output); + viewer.tx.output_data(DEPOSIT_CELL_DATA.clone()); } Ok(()) @@ -187,15 +309,17 @@ impl DaoScriptHandler { impl ScriptHandler for DaoScriptHandler { fn prepare_transaction( &self, - transaction_inputs: &mut Vec, - tx_data: &mut crate::core::TransactionBuilder, - context: &dyn HandlerContext, + viewer: &mut PrepareTransactionViewer, + context: &mut dyn HandlerContext, ) -> Result { if let Some(args) = context.as_any().downcast_ref::() { - Self::build_deposit(transaction_inputs, tx_data, args)?; + Self::build_deposit(viewer, args)?; Ok(true) } else if let Some(args) = context.as_any().downcast_ref::() { - Self::build_phrase1_base(transaction_inputs, tx_data, args)?; + Self::build_phrase1_base(viewer, args)?; + Ok(true) + } else if let Some(args) = context.as_mut().downcast_mut::() { + Self::build_phrase2_base(viewer, args)?; Ok(true) } else { Ok(false) @@ -210,18 +334,20 @@ impl ScriptHandler for DaoScriptHandler { if !self.is_match(&script_group.script) { return Ok(false); } - if let Some(_args) = context.as_any().downcast_ref::() { + if context.as_any().is::() + || context.as_any().is::() + { tx_data.dedup_cell_deps(self.cell_deps.clone()); - if !script_group.input_indices.is_empty() { - let index = script_group.input_indices.first().unwrap(); - let witness = WitnessArgs::new_builder() - .lock(Some(bytes::Bytes::from(vec![0u8; 65])).pack()) - .build(); - tx_data.set_witness(*index, witness.as_bytes().pack()); - } Ok(true) - } else if let Some(_args) = context.as_any().downcast_ref::() { + } else if let Some(args) = context.as_any().downcast_ref::() { tx_data.dedup_cell_deps(self.cell_deps.clone()); + if let Some(idx) = script_group.input_indices.last() { + if let Some(dep_header_idx) = args.deposit_header_indexes.get(idx) { + let idx_data = + bytes::Bytes::from((*dep_header_idx as u64).to_le_bytes().to_vec()); + tx_data.set_witness_input(*idx, Some(idx_data)); + } + } Ok(true) } else { Ok(false) diff --git a/src/transaction/handler/mod.rs b/src/transaction/handler/mod.rs index 85cbd335..dcb82941 100644 --- a/src/transaction/handler/mod.rs +++ b/src/transaction/handler/mod.rs @@ -7,7 +7,7 @@ use crate::{ use self::sighash::Secp256k1Blake160SighashAllScriptContext; -use super::input::TransactionInput; +use super::builder::PrepareTransactionViewer; pub mod dao; pub mod multisig; @@ -16,9 +16,8 @@ pub mod sighash; pub trait ScriptHandler { fn prepare_transaction( &self, - _transaction_inputs: &mut Vec, - _tx_data: &mut TransactionBuilder, - _context: &dyn HandlerContext, + _viewer: &mut PrepareTransactionViewer, + _context: &mut dyn HandlerContext, ) -> Result { Ok(false) } @@ -38,12 +37,17 @@ pub trait ScriptHandler { pub trait Type2Any: 'static { fn as_any(&self) -> &dyn Any; + fn as_mut(&mut self) -> &mut dyn Any; } impl Type2Any for T { fn as_any(&self) -> &dyn Any { self } + + fn as_mut(&mut self) -> &mut dyn Any { + self + } } pub trait HandlerContext: Type2Any {} diff --git a/src/transaction/handler/multisig.rs b/src/transaction/handler/multisig.rs index 046078bb..e9755893 100644 --- a/src/transaction/handler/multisig.rs +++ b/src/transaction/handler/multisig.rs @@ -55,8 +55,8 @@ impl ScriptHandler for Secp256k1Blake160MultisigAllScriptHandler { { tx_builder.dedup_cell_deps(self.cell_deps.clone()); let index = script_group.input_indices.first().unwrap(); - let witness = args.multisig_config.placeholder_witness(); - tx_builder.set_witness(*index, witness.as_bytes().pack()); + let witness_lock = args.multisig_config.placeholder_witness_lock(); + tx_builder.set_witness_lock(*index, witness_lock); Ok(true) } else { Ok(false) diff --git a/src/transaction/handler/sighash.rs b/src/transaction/handler/sighash.rs index 51a8308c..159d724e 100644 --- a/src/transaction/handler/sighash.rs +++ b/src/transaction/handler/sighash.rs @@ -1,7 +1,7 @@ use ckb_types::{ core::DepType, h256, - packed::{CellDep, OutPoint, Script, WitnessArgs}, + packed::{CellDep, OutPoint, Script}, prelude::{Builder, Entity, Pack}, }; @@ -47,10 +47,7 @@ impl ScriptHandler for Secp256k1Blake160SighashAllScriptHandler { { tx_builder.dedup_cell_deps(self.cell_deps.clone()); let index = script_group.input_indices.first().unwrap(); - let witness = WitnessArgs::new_builder() - .lock(Some(bytes::Bytes::from(vec![0u8; 65])).pack()) - .build(); - tx_builder.set_witness(*index, witness.as_bytes().pack()); + tx_builder.set_witness_lock(*index, Some(bytes::Bytes::from(vec![0u8; 65]))); Ok(true) } else { Ok(false) diff --git a/src/unlock/signer.rs b/src/unlock/signer.rs index 52a55b55..b86379cd 100644 --- a/src/unlock/signer.rs +++ b/src/unlock/signer.rs @@ -230,12 +230,16 @@ impl MultisigConfig { } pub fn placeholder_witness(&self) -> WitnessArgs { + WitnessArgs::new_builder() + .lock(self.placeholder_witness_lock().pack()) + .build() + } + + pub fn placeholder_witness_lock(&self) -> Option { let config_data = self.to_witness_data(); let mut zero_lock = vec![0u8; config_data.len() + 65 * self.threshold() as usize]; zero_lock[0..config_data.len()].copy_from_slice(config_data.as_ref()); - WitnessArgs::new_builder() - .lock(Some(Bytes::from(zero_lock)).pack()) - .build() + Some(Bytes::from(zero_lock)) } pub fn to_address(&self, network: NetworkType, since_absolute_epoch: Option) -> Address {