From 25a470f7219a6ed0fab55e055a145559813c1628 Mon Sep 17 00:00:00 2001 From: Liu Chuankai Date: Wed, 10 May 2023 17:44:41 +0800 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20support=20dao=20de?= =?UTF-8?q?posit=20script=20simple=20transaction=20builder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/dao_deposit_example.rs | 52 +++++++ src/transaction/builder/mod.rs | 25 ++++ src/transaction/handler/dao.rs | 244 ++++++++++++++++++++++++++++++++ src/transaction/handler/mod.rs | 36 ++++- src/transaction/mod.rs | 1 + 5 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 examples/dao_deposit_example.rs create mode 100644 src/transaction/handler/dao.rs diff --git a/examples/dao_deposit_example.rs b/examples/dao_deposit_example.rs new file mode 100644 index 00000000..61d04238 --- /dev/null +++ b/examples/dao_deposit_example.rs @@ -0,0 +1,52 @@ +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 receiver=Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqv5dsed9par23x4g58seaw58j3ym5ml2hs8ztche")?; + + 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 context = dao::DepositContext::new((&receiver).into(), 510_0000_0000u64); + 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(&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: 8b7ab7770c821fa8dc70738d5d6ef43da46706541c258b9a02edf66948039798 + println!(">>> tx {} sent! <<<", tx_hash); + + Ok(()) +} diff --git a/src/transaction/builder/mod.rs b/src/transaction/builder/mod.rs index fd98b3de..f7de29be 100644 --- a/src/transaction/builder/mod.rs +++ b/src/transaction/builder/mod.rs @@ -132,6 +132,25 @@ impl SimpleTransactionBuilder { .capacity((capacity + delta_capacity).pack()) .build(); tx_builder.set_output(idx, output); + + Ok(()) + } + + fn prepare_transaction( + transaction_inputs: &mut Vec, + tx_builder: &mut crate::core::TransactionBuilder, + configuration: &TransactionBuilderConfiguration, + contexts: &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()) + { + break; + } + } + } Ok(()) } } @@ -148,6 +167,12 @@ impl CkbTransactionBuilder for SimpleTransactionBuilder { &mut self, contexts: &HandlerContexts, ) -> Result { + Self::prepare_transaction( + &mut self.transaction_inputs, + &mut self.tx, + &self.configuration, + contexts, + )?; let mut lock_groups: HashMap = HashMap::default(); let mut type_groups: HashMap = HashMap::default(); let mut outputs_capacity = 0u64; diff --git a/src/transaction/handler/dao.rs b/src/transaction/handler/dao.rs new file mode 100644 index 00000000..18c637a5 --- /dev/null +++ b/src/transaction/handler/dao.rs @@ -0,0 +1,244 @@ +use anyhow::anyhow; +use ckb_types::{ + core::{DepType, ScriptHashType}, + h256, + packed::{CellDep, CellInput, CellOutput, OutPoint, Script, WitnessArgs}, + prelude::{Builder, Entity, Pack}, +}; +use lazy_static::lazy_static; + +use crate::{ + constants, + traits::{ + DefaultHeaderDepResolver, DefaultTransactionDependencyProvider, HeaderDepResolver, LiveCell, + }, + transaction::input::TransactionInput, + tx_builder::{ + dao::{DaoDepositReceiver, DaoPrepareItem}, + TxBuilderError, + }, + NetworkInfo, NetworkType, ScriptGroup, +}; + +use super::{HandlerContext, ScriptHandler}; + +lazy_static! { + static ref DAO_TYPE_SCRIPT: Script = Script::new_builder() + .code_hash(constants::DAO_TYPE_HASH.pack()) + .hash_type(ScriptHashType::Type.into()) + .build(); +} + +pub struct DaoScriptHandler { + cell_deps: Vec, +} + +#[derive(Clone, Debug, Default)] +pub struct DepositContext { + // lock script, capacity list + pub receivers: Vec, +} + +impl DepositContext { + pub fn new(lock_script: Script, capacity: u64) -> Self { + let mut ret = Self::default(); + ret.add_output(lock_script, capacity); + ret + } + + pub fn add_output(&mut self, lock_script: Script, capacity: u64) { + self.receivers + .push(DaoDepositReceiver::new(lock_script, capacity)); + } +} + +impl HandlerContext for DepositContext {} + +#[derive(Default)] +pub struct WithdrawPhrase1Context { + pub items: Vec, + pub rpc_url: String, +} + +impl WithdrawPhrase1Context { + /// add input. + /// If `lock_script` is `None` copy the lock script from input with same + /// index, otherwise replace the lock script with the given script. + pub fn add_input(&mut self, input: CellInput, lock_script: Option