-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #77 from quake/quake/type-id-handler
feat: add TypeId handler
- Loading branch information
Showing
11 changed files
with
235 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use ckb_sdk::{ | ||
constants, | ||
transaction::{ | ||
builder::{CkbTransactionBuilder, SimpleTransactionBuilder}, | ||
input::InputIterator, | ||
signer::{SignContexts, TransactionSigner}, | ||
TransactionBuilderConfiguration, | ||
}, | ||
Address, CkbRpcClient, NetworkInfo, ScriptId, | ||
}; | ||
use ckb_types::{ | ||
core::Capacity, | ||
h256, | ||
packed::{Bytes, CellOutput}, | ||
prelude::*, | ||
}; | ||
use std::{ | ||
error::Error as StdErr, | ||
fs::File, | ||
io::{BufReader, Read}, | ||
str::FromStr, | ||
}; | ||
|
||
fn main() -> Result<(), Box<dyn StdErr>> { | ||
let network_info = NetworkInfo::testnet(); | ||
let configuration = TransactionBuilderConfiguration::new_with_network(network_info.clone())?; | ||
|
||
let deployer = Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq2qf8keemy2p5uu0g0gn8cd4ju23s5269qk8rg4r")?; | ||
let (output, data) = build_output_and_data(&deployer); | ||
|
||
let iterator = InputIterator::new_with_address(&[deployer], &network_info); | ||
let mut builder = SimpleTransactionBuilder::new(configuration, iterator); | ||
builder.add_output_and_data(output, data); | ||
|
||
let mut tx_with_groups = builder.build(&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()); | ||
|
||
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"); | ||
|
||
println!(">>> tx {} sent! <<<", tx_hash); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn build_output_and_data(deployer: &Address) -> (CellOutput, Bytes) { | ||
let script_binary = File::open("./src/test-data/always_success").unwrap(); | ||
let mut reader = BufReader::new(script_binary); | ||
let mut buffer = Vec::new(); | ||
reader.read_to_end(&mut buffer).unwrap(); | ||
let data_capacity = Capacity::bytes(buffer.len()).unwrap(); | ||
|
||
let type_script = | ||
ScriptId::new_type(constants::TYPE_ID_CODE_HASH.clone()).dummy_type_id_script(); | ||
let dummy_output = CellOutput::new_builder() | ||
.lock(deployer.into()) | ||
.type_(Some(type_script).pack()) | ||
.build(); | ||
let required_capacity = dummy_output | ||
.occupied_capacity(data_capacity) | ||
.unwrap() | ||
.pack(); | ||
let output = dummy_output | ||
.as_builder() | ||
.capacity(required_capacity) | ||
.build(); | ||
(output, buffer.pack()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
pub mod sighash; | ||
pub mod typeid; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
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_type_id_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_and_data(output, bytes::Bytes::from(vec![0x01u8; 64]).pack()); | ||
|
||
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use ckb_hash::new_blake2b; | ||
use ckb_types::{ | ||
packed::{Bytes, CellInput}, | ||
prelude::*, | ||
}; | ||
|
||
use crate::{ | ||
core::TransactionBuilder, tx_builder::TxBuilderError, NetworkInfo, ScriptGroup, ScriptId, | ||
}; | ||
|
||
use super::{HandlerContext, ScriptHandler}; | ||
|
||
/// Type ID script handler, it will setup the [Type ID](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0022-transaction-structure/0022-transaction-structure.md#type-id) script's args automatically. | ||
pub struct TypeIdHandler; | ||
|
||
pub struct TypeIdContext; | ||
|
||
impl HandlerContext for TypeIdContext {} | ||
|
||
impl ScriptHandler for TypeIdHandler { | ||
fn build_transaction( | ||
&self, | ||
tx_builder: &mut TransactionBuilder, | ||
script_group: &mut ScriptGroup, | ||
context: &dyn HandlerContext, | ||
) -> Result<bool, TxBuilderError> { | ||
if context.as_any().is::<TypeIdContext>() | ||
&& ScriptId::from(&script_group.script).is_type_id() | ||
&& script_group.input_indices.is_empty() | ||
&& script_group.output_indices.len() == 1 | ||
{ | ||
let input = tx_builder.get_inputs().first().unwrap(); | ||
let index = *script_group.output_indices.last().unwrap(); | ||
let args: Bytes = calculate_type_id(input, index as u64).to_vec().pack(); | ||
let output = tx_builder.get_outputs().get(index).unwrap().clone(); | ||
let output_type_script = output | ||
.type_() | ||
.to_opt() | ||
.unwrap() | ||
.as_builder() | ||
.args(args) | ||
.build(); | ||
let updated_output = output | ||
.as_builder() | ||
.type_(Some(output_type_script.clone()).pack()) | ||
.build(); | ||
tx_builder.set_output(index, updated_output); | ||
script_group.script = output_type_script; | ||
return Ok(true); | ||
} | ||
Ok(false) | ||
} | ||
|
||
fn init(&mut self, _network: &NetworkInfo) -> Result<(), TxBuilderError> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters