diff --git a/Cargo.toml b/Cargo.toml index 34998c30..22b1a1a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ faster-hex = "0.9.0" ripemd = "0.1.3" sha2 = "0.10.6" ed25519-dalek = "2.1.1" -openssl = "0.10" ckb-types = "0.119.0" ckb-dao-utils = "0.119.0" diff --git a/examples/omnilock_examples.rs b/examples/omnilock_examples.rs index fb65bc2b..b8e4edf8 100644 --- a/examples/omnilock_examples.rs +++ b/examples/omnilock_examples.rs @@ -40,7 +40,7 @@ fn main() -> Result<(), Box> { let mut contexts = HandlerContexts::default(); contexts.add_context(Box::new(context) as Box<_>); - let mut tx_with_groups = builder.build(&mut contexts)?; + 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()); diff --git a/examples/omnilock_multisig_example.rs b/examples/omnilock_multisig_example.rs index e8896f84..beae02e6 100644 --- a/examples/omnilock_multisig_example.rs +++ b/examples/omnilock_multisig_example.rs @@ -50,7 +50,7 @@ fn main() -> Result<(), Box> { let mut contexts = HandlerContexts::default(); contexts.add_context(Box::new(context) as Box<_>); - let mut tx_with_groups = builder.build(&mut contexts)?; + 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()); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 1af12c4a..e1353007 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -50,6 +50,7 @@ const ACP_BIN: &[u8] = include_bytes!("../test-data/anyone_can_pay"); const CHEQUE_BIN: &[u8] = include_bytes!("../test-data/ckb-cheque-script"); const ALWAYS_SUCCESS_BIN: &[u8] = include_bytes!("../test-data/always_success"); const OMNILOCK_BIN: &[u8] = include_bytes!("../test-data/omni_lock"); +// const ALWAYS_SUCCESS_BIN_DL: &[u8] = include_bytes!("../test-data/always_success_dl"); fn build_sighash_script(args: H160) -> Script { Script::new_builder() @@ -67,6 +68,22 @@ fn build_multisig_script(cfg: &MultisigConfig) -> Script { .build() } +fn build_always_success_script() -> Script { + let data_hash = H256::from(blake2b_256(ALWAYS_SUCCESS_BIN)); + Script::new_builder() + .code_hash(data_hash.pack()) + .hash_type(ScriptHashType::Data1.into()) + .build() +} + +// fn build_always_success_script_dl() -> Script { +// let data_hash = H256::from(blake2b_256(ALWAYS_SUCCESS_BIN_DL)); +// Script::new_builder() +// .code_hash(data_hash.pack()) +// .hash_type(ScriptHashType::Data1.into()) +// .build() +// } + fn build_dao_script() -> Script { Script::new_builder() .code_hash(DAO_TYPE_HASH.pack()) diff --git a/src/tests/transaction/omnilock.rs b/src/tests/transaction/omnilock.rs index 9c2d05ee..990e5014 100644 --- a/src/tests/transaction/omnilock.rs +++ b/src/tests/transaction/omnilock.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; use ckb_crypto::secp::{Pubkey, SECP256K1}; use ckb_hash::blake2b_256; use ckb_types::{ - core::DepType, + core::{DepType, TransactionView}, packed::{CellOutput, OutPoint}, prelude::*, H160, H256, @@ -12,9 +12,11 @@ use ckb_types::{ use crate::{ constants::ONE_CKB, tests::{ - build_omnilock_script, build_sighash_script, init_context, ACCOUNT0_ARG, ACCOUNT0_KEY, - ACCOUNT1_ARG, ACCOUNT1_KEY, ACCOUNT2_ARG, FEE_RATE, OMNILOCK_BIN, + build_always_success_script, build_omnilock_script, build_sighash_script, init_context, + ACCOUNT0_ARG, ACCOUNT0_KEY, ACCOUNT1_ARG, ACCOUNT1_KEY, ACCOUNT2_ARG, ALWAYS_SUCCESS_BIN, + FEE_RATE, OMNILOCK_BIN, }, + traits::{Signer, SignerError}, transaction::{ builder::{CkbTransactionBuilder, SimpleTransactionBuilder}, handler::{ @@ -28,12 +30,15 @@ use crate::{ signer::{SignContexts, TransactionSigner}, TransactionBuilderConfiguration, }, - unlock::{MultisigConfig, OmniLockConfig}, + unlock::{ + omni_lock::{ExecDlConfig, Preimage}, + MultisigConfig, OmniLockConfig, + }, util::{blake160, keccak160}, NetworkInfo, }; -fn test_omnilock_config(omnilock_outpoint: OutPoint) -> TransactionBuilderConfiguration { +fn test_omnilock_config(outpoints: Vec) -> TransactionBuilderConfiguration { let network_info = NetworkInfo::testnet(); let mut configuration = TransactionBuilderConfiguration::new_with_empty_handlers(network_info.clone()); @@ -42,17 +47,24 @@ fn test_omnilock_config(omnilock_outpoint: OutPoint) -> TransactionBuilderConfig omni_lock_handler.set_lock_script_id(crate::ScriptId::new_data1(H256::from(blake2b_256( OMNILOCK_BIN, )))); - omni_lock_handler.set_cell_deps(vec![ - crate::transaction::handler::cell_dep!( + let dep_cells = { + let mut cells = Vec::with_capacity(outpoints.len() + 1); + cells.push(crate::transaction::handler::cell_dep!( "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37", 0u32, DepType::DepGroup - ), - ckb_types::packed::CellDep::new_builder() - .out_point(omnilock_outpoint) - .dep_type(DepType::Code.into()) - .build(), - ]); + )); + for outpoint in outpoints { + cells.push( + ckb_types::packed::CellDep::new_builder() + .out_point(outpoint) + .dep_type(DepType::Code.into()) + .build(), + ) + } + cells + }; + omni_lock_handler.set_cell_deps(dep_cells); configuration.register_script_handler(Box::new( Secp256k1Blake160SighashAllScriptHandler::new_with_network(&network_info).unwrap(), @@ -295,7 +307,7 @@ fn omnilock_test(cfg: OmniLockConfig, sign_context: &SignContexts) { let sender = build_omnilock_script(&cfg); let receiver = build_sighash_script(ACCOUNT2_ARG); - let (ctx, mut outpoints) = init_context( + let (ctx, outpoints) = init_context( vec![(OMNILOCK_BIN, true)], vec![ (sender.clone(), Some(100 * ONE_CKB)), @@ -304,7 +316,7 @@ fn omnilock_test(cfg: OmniLockConfig, sign_context: &SignContexts) { ], ); - let configuration = test_omnilock_config(outpoints.pop().unwrap()); + let configuration = test_omnilock_config(outpoints); let iterator = InputIterator::new_with_cell_collector( vec![sender.clone()], @@ -374,14 +386,14 @@ fn test_omnilock_owner_lock_tranfer(cobuild: bool) { let mut cfg = OmniLockConfig::new_ownerlock(hash); cfg.enable_cobuild(cobuild); let sender0 = build_omnilock_script(&cfg); - let mut sign_context = SignContexts::new_omnilock(vec![account0_key.clone()], cfg.clone()); + let mut sign_context = SignContexts::new_omnilock(vec![account0_key], cfg.clone()); let hashall_unlock = crate::transaction::signer::sighash::Secp256k1Blake160SighashAllSignerContext::new(vec![ - account0_key.clone(), + account0_key, ]); sign_context.add_context(Box::new(hashall_unlock)); - let (ctx, mut outpoints) = init_context( + let (ctx, outpoints) = init_context( vec![(OMNILOCK_BIN, true)], vec![ (sender0.clone(), Some(150 * ONE_CKB)), @@ -389,7 +401,7 @@ fn test_omnilock_owner_lock_tranfer(cobuild: bool) { ], ); - let configuration = test_omnilock_config(outpoints.pop().unwrap()); + let configuration = test_omnilock_config(outpoints); let iterator = InputIterator::new_with_cell_collector( vec![sender0.clone(), sender1.clone()], Box::new(ctx.to_live_cells_context()) as Box<_>, @@ -443,3 +455,116 @@ fn test_omnilock_owner_lock_tranfer(cobuild: bool) { assert_eq!(tx.data().as_reader().serialized_size_in_block() as u64, fee); ctx.verify(tx, FEE_RATE).unwrap(); } + +#[derive(Clone)] +struct DummySinger {} + +impl Signer for DummySinger { + fn match_id(&self, id: &[u8]) -> bool { + let always_success_script = build_always_success_script(); + let preimage = + Preimage::new_with_exec(always_success_script, 0, [0; 8], blake160(&[0u8; 20])); + id.len() == 20 && id == preimage.auth().as_bytes() + } + + fn sign( + &self, + _id: &[u8], + _message: &[u8], + _recoverable: bool, + _tx: &TransactionView, + ) -> Result { + Ok(bytes::Bytes::from(vec![0; 65])) + } +} + +#[test] +fn test_omnilock_exec() { + let always_success_script = build_always_success_script(); + let preimage = Preimage::new_with_exec(always_success_script, 0, [0; 8], blake160(&[0u8; 20])); + let config = ExecDlConfig::new(preimage, 65); + + test_omnilock_dl_exec(config.clone(), false); + test_omnilock_dl_exec(config, true) +} + +#[ignore] +#[test] +fn test_omnilock_dl() { + // let always_success_script = build_always_success_script_dl(); + // let preimage = Preimage::new_with_dl(always_success_script, blake160(&[0u8; 20])); + // test_omnilock_dl_exec(preimage) +} + +fn test_omnilock_dl_exec(config: ExecDlConfig, cobuild: bool) { + let network_info = NetworkInfo::testnet(); + let receiver = build_sighash_script(ACCOUNT2_ARG); + + let mut cfg = if config.preimage().len() == 32 + 1 + 1 + 8 + 20 { + OmniLockConfig::new_with_exec_preimage(config) + } else { + OmniLockConfig::new_with_dl_preimage(config) + }; + + cfg.enable_cobuild(cobuild); + let sender = build_omnilock_script(&cfg); + let sign_context = SignContexts::new_omnilock_exec_dl_custom(DummySinger {}, cfg.clone()); + + let (ctx, outpoints) = init_context( + vec![(OMNILOCK_BIN, true), (ALWAYS_SUCCESS_BIN, true)], + vec![(sender.clone(), Some(300 * ONE_CKB))], + ); + + let configuration = test_omnilock_config(outpoints); + + 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); + + let output = CellOutput::new_builder() + .capacity((120 * ONE_CKB).pack()) + .lock(receiver) + .build(); + builder.add_output_and_data(output.clone(), ckb_types::packed::Bytes::default()); + builder.set_change_lock(sender.clone()); + + let context = OmnilockScriptContext::new(cfg.clone(), network_info.url.clone()); + let mut contexts = HandlerContexts::default(); + contexts.add_context(Box::new(context) as Box<_>); + + let mut tx_with_groups = builder.build(&contexts).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) + // use unitest lock to verify + .insert_unlocker( + crate::ScriptId::new_data1(H256::from(blake2b_256(OMNILOCK_BIN))), + crate::transaction::signer::omnilock::OmnilockSigner {}, + ) + .sign_transaction(&mut tx_with_groups, &sign_context) + .unwrap(); + let tx = tx_with_groups.get_tx_view().clone(); + let script_groups = tx_with_groups.script_groups.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()); + + assert_eq!(script_groups.len(), 1); + assert_eq!(tx.header_deps().len(), 0); + assert_eq!(tx.cell_deps().len(), 3); + 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); + let change_capacity: u64 = tx.output(1).unwrap().capacity().unpack(); + let fee = (300 - 120) * ONE_CKB - change_capacity; + assert_eq!(tx.data().as_reader().serialized_size_in_block() as u64, fee); + ctx.verify(tx, FEE_RATE).unwrap(); +} diff --git a/src/traits/default_impls.rs b/src/traits/default_impls.rs index f468ef05..013243c8 100644 --- a/src/traits/default_impls.rs +++ b/src/traits/default_impls.rs @@ -19,15 +19,13 @@ use ckb_types::{ prelude::*, H160, }; -use openssl::pkey::{PKey, Private}; use super::{ offchain_impls::CollectResult, OffchainCellCollector, OffchainCellDepResolver, OffchainTransactionDependencyProvider, }; -use crate::util::{ - get_max_mature_number, iso9796_2_batch_sign, serialize_signature, zeroize_privkey, -}; +use crate::types::ScriptId; +use crate::util::{get_max_mature_number, serialize_signature, zeroize_privkey}; use crate::SECP256K1; use crate::{ constants::{ @@ -53,7 +51,6 @@ use crate::{ }, util::blake160, }; -use crate::{types::ScriptId, util::rsa_sign}; use ckb_resource::{ CODE_HASH_DAO, CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL, CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL, @@ -703,53 +700,6 @@ impl Drop for SecpCkbRawKeySigner { } } -pub struct RsaSigner { - key: PKey, - use_rsa: bool, - use_iso9796_2: bool, -} - -impl RsaSigner { - pub fn new(key: PKey, use_rsa: bool, use_iso9796_2: bool) -> Self { - Self { - key, - use_rsa, - use_iso9796_2, - } - } -} - -impl Signer for RsaSigner { - fn match_id(&self, id: &[u8]) -> bool { - id.len() == 20 && id == blake160(&self.key.public_key_to_pem().unwrap()).as_bytes() - } - - fn sign( - &self, - id: &[u8], - message: &[u8], - _recoverable: bool, - _tx: &TransactionView, - ) -> Result { - if !self.match_id(id) { - return Err(SignerError::IdNotFound); - } - if message.len() != 32 { - return Err(SignerError::InvalidMessage(format!( - "expected length: 32, got: {}", - message.len() - ))); - } - if self.use_rsa { - Ok(rsa_sign(message, &self.key).into()) - } else if self.use_iso9796_2 { - Ok(iso9796_2_batch_sign(message, &self.key).into()) - } else { - Ok(Default::default()) - } - } -} - /// A signer use ed25519 raw key #[derive(Clone)] pub struct Ed25519Signer { diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 82d68282..52d1f34b 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -59,7 +59,7 @@ pub enum SignerError { /// * secp256k1 eth signer /// * RSA signer /// * Hardware wallet signer -pub trait Signer { +pub trait Signer: DynClone { /// typecial id are blake160(pubkey) and keccak256(pubkey)[12..20] fn match_id(&self, id: &[u8]) -> bool; diff --git a/src/transaction/signer/mod.rs b/src/transaction/signer/mod.rs index 830997d4..67903b26 100644 --- a/src/transaction/signer/mod.rs +++ b/src/transaction/signer/mod.rs @@ -1,13 +1,9 @@ -use ckb_types::{ - core, - packed::{CellOutput, OutPoint}, - H256, -}; -use openssl::pkey::{PKey, Private}; +use ckb_types::{core, packed, H256}; use std::collections::HashMap; use crate::{ constants, + traits::TransactionDependencyProvider, unlock::{MultisigConfig, OmniLockConfig, UnlockError}, NetworkInfo, NetworkType, ScriptGroup, ScriptId, TransactionWithScriptGroups, }; @@ -27,7 +23,7 @@ pub trait CKBScriptSigner { tx_view: &core::TransactionView, script_group: &ScriptGroup, context: &dyn SignContext, - inputs: &HashMap, + inputs: &dyn TransactionDependencyProvider, ) -> Result; } @@ -94,9 +90,12 @@ impl SignContexts { } } - pub fn new_omnilock_dl(key: PKey, omnilock_config: OmniLockConfig) -> Self { + pub fn new_omnilock_exec_dl_custom( + signer: T, + omnilock_config: OmniLockConfig, + ) -> Self { let omnilock_context = - omnilock::OmnilockSignerContext::new_with_rsa_key(key, omnilock_config); + omnilock::OmnilockSignerContext::new_with_dl_exec_signer(signer, omnilock_config); Self { contexts: vec![Box::new(omnilock_context)], } @@ -173,7 +172,9 @@ impl TransactionSigner { &tx, script_group, context.as_ref(), - &transaction.inputs, + &InputsProvider { + inputs: &transaction.inputs, + }, )?; signed_groups_indices.push(idx); break; @@ -184,3 +185,56 @@ impl TransactionSigner { Ok(signed_groups_indices) } } + +struct InputsProvider<'a> { + inputs: &'a HashMap, +} + +impl<'a> crate::traits::TransactionDependencyProvider for InputsProvider<'a> { + /// For verify certain cell belong to certain transaction + fn get_transaction( + &self, + _tx_hash: &packed::Byte32, + ) -> Result { + Err(crate::traits::TransactionDependencyError::NotFound( + "not support".to_string(), + )) + } + /// For get the output information of inputs or cell_deps, those cell should be live cell + fn get_cell( + &self, + out_point: &packed::OutPoint, + ) -> Result { + self.inputs.get(out_point).map(|a| a.0.clone()).ok_or( + crate::traits::TransactionDependencyError::NotFound("not found".to_string()), + ) + } + /// For get the output data information of inputs or cell_deps + fn get_cell_data( + &self, + out_point: &packed::OutPoint, + ) -> Result { + self.inputs.get(out_point).map(|a| a.1.clone()).ok_or( + crate::traits::TransactionDependencyError::NotFound("not found".to_string()), + ) + } + /// For get the header information of header_deps + fn get_header( + &self, + _block_hash: &packed::Byte32, + ) -> Result { + Err(crate::traits::TransactionDependencyError::NotFound( + "not support".to_string(), + )) + } + + /// For get_block_extension + fn get_block_extension( + &self, + _block_hash: &packed::Byte32, + ) -> Result, crate::traits::TransactionDependencyError> { + Err(crate::traits::TransactionDependencyError::NotFound( + "not support".to_string(), + )) + } +} diff --git a/src/transaction/signer/multisig.rs b/src/transaction/signer/multisig.rs index c9e6ac2d..3b67e4e3 100644 --- a/src/transaction/signer/multisig.rs +++ b/src/transaction/signer/multisig.rs @@ -1,9 +1,10 @@ -use std::collections::HashMap; - -use ckb_types::{core, packed}; +use ckb_types::core; use crate::{ - traits::{dummy_impls::DummyTransactionDependencyProvider, SecpCkbRawKeySigner}, + traits::{ + dummy_impls::DummyTransactionDependencyProvider, SecpCkbRawKeySigner, + TransactionDependencyProvider, + }, unlock::{ MultisigConfig, ScriptUnlocker, SecpMultisigScriptSigner, SecpMultisigUnlocker, UnlockError, }, @@ -47,7 +48,7 @@ impl CKBScriptSigner for Secp256k1Blake160MultisigAllSigner { transaction: &core::TransactionView, script_group: &crate::ScriptGroup, context: &dyn super::SignContext, - _inputs: &HashMap, + _tx_dep_provider: &dyn TransactionDependencyProvider, ) -> Result { if let Some(args) = context .as_any() diff --git a/src/transaction/signer/omnilock.rs b/src/transaction/signer/omnilock.rs index a0493af3..51863208 100644 --- a/src/transaction/signer/omnilock.rs +++ b/src/transaction/signer/omnilock.rs @@ -1,12 +1,10 @@ use std::collections::HashMap; use ckb_types::{core, packed}; -use openssl::pkey::{PKey, Private}; use crate::{ traits::{ - default_impls::{Ed25519Signer, RsaSigner}, - SecpCkbRawKeySigner, Signer, + default_impls::Ed25519Signer, SecpCkbRawKeySigner, Signer, TransactionDependencyProvider, }, unlock::{ IdentityFlag, OmniLockConfig, OmniLockScriptSigner, OmniLockUnlocker, OmniUnlockMode, @@ -21,7 +19,7 @@ pub struct OmnilockSigner {} pub struct OmnilockSignerContext { keys: Vec, ed25519_key: Option, - rsa_key: Option>, + custom_signer: Option>, cfg: OmniLockConfig, unlock_mode: OmniUnlockMode, } @@ -31,7 +29,7 @@ impl OmnilockSignerContext { Self { keys, ed25519_key: None, - rsa_key: None, + custom_signer: None, cfg, unlock_mode: OmniUnlockMode::Normal, } @@ -41,17 +39,17 @@ impl OmnilockSignerContext { Self { keys: Default::default(), ed25519_key: Some(key), - rsa_key: None, + custom_signer: None, cfg, unlock_mode: OmniUnlockMode::Normal, } } - pub fn new_with_rsa_key(key: PKey, cfg: OmniLockConfig) -> Self { + pub fn new_with_dl_exec_signer(signer: T, cfg: OmniLockConfig) -> Self { Self { keys: Default::default(), ed25519_key: None, - rsa_key: Some(key), + custom_signer: Some(Box::new(signer)), cfg, unlock_mode: OmniUnlockMode::Normal, } @@ -77,16 +75,20 @@ impl OmnilockSignerContext { IdentityFlag::Solana => Box::new(Ed25519Signer::new( self.ed25519_key.clone().expect("must have ed25519"), )), - IdentityFlag::Dl => Box::new(RsaSigner::new( - self.rsa_key.clone().expect("muse have rsa"), - self.cfg.use_rsa, - self.cfg.use_iso9796_2, - )), IdentityFlag::OwnerLock => Box::new(SecpCkbRawKeySigner::new_with_owner_lock( self.keys.clone(), self.cfg.id().auth_content().clone(), )), - _ => Box::new(SecpCkbRawKeySigner::new_with_secret_keys(self.keys.clone())), + IdentityFlag::Dl | IdentityFlag::Exec => { + let signer = self + .custom_signer + .as_ref() + .expect("must have custom signer"); + dyn_clone::clone_box(&**signer) + } + IdentityFlag::Multisig | IdentityFlag::PubkeyHash => { + Box::new(SecpCkbRawKeySigner::new_with_secret_keys(self.keys.clone())) + } }; let omnilock_signer = OmniLockScriptSigner::new(signer, self.cfg.clone(), self.unlock_mode); OmniLockUnlocker::new(omnilock_signer, self.cfg.clone()) @@ -104,11 +106,11 @@ impl CKBScriptSigner for OmnilockSigner { transaction: &core::TransactionView, script_group: &crate::ScriptGroup, context: &dyn super::SignContext, - inputs: &HashMap, + tx_dep_provider: &dyn TransactionDependencyProvider, ) -> Result { if let Some(args) = context.as_any().downcast_ref::() { let unlocker = args.build_omnilock_unlocker(); - let tx = unlocker.unlock(transaction, script_group, &InputsProvider { inputs } as _)?; + let tx = unlocker.unlock(transaction, script_group, tx_dep_provider)?; Ok(tx) } else { Err(UnlockError::SignContextTypeIncorrect) diff --git a/src/transaction/signer/sighash.rs b/src/transaction/signer/sighash.rs index 7180142f..b564e434 100644 --- a/src/transaction/signer/sighash.rs +++ b/src/transaction/signer/sighash.rs @@ -1,9 +1,10 @@ -use std::collections::HashMap; - -use ckb_types::{core, packed}; +use ckb_types::core; use crate::{ - traits::{dummy_impls::DummyTransactionDependencyProvider, SecpCkbRawKeySigner}, + traits::{ + dummy_impls::DummyTransactionDependencyProvider, SecpCkbRawKeySigner, + TransactionDependencyProvider, + }, unlock::{ScriptUnlocker, SecpSighashUnlocker, UnlockError}, }; @@ -34,7 +35,7 @@ impl CKBScriptSigner for Secp256k1Blake160SighashAllSigner { transaction: &core::TransactionView, script_group: &crate::ScriptGroup, context: &dyn super::SignContext, - _inputs: &HashMap, + _tx_dep_provider: &dyn TransactionDependencyProvider, ) -> Result { if let Some(args) = context .as_any() diff --git a/src/unlock/omni_lock.rs b/src/unlock/omni_lock.rs index 22a0f02b..8ea1e862 100644 --- a/src/unlock/omni_lock.rs +++ b/src/unlock/omni_lock.rs @@ -16,6 +16,7 @@ use crate::{ }; use bitflags::bitflags; +use ckb_types::packed::Script; pub use ckb_types::prelude::Pack; use ckb_types::{ bytes::{BufMut, Bytes, BytesMut}, @@ -24,7 +25,6 @@ use ckb_types::{ H160, H256, }; use enum_repr_derive::{FromEnumToRepr, TryFromReprToEnum}; -use openssl::pkey::{PKey, Public}; use serde::{de::Unexpected, Deserialize, Serialize}; use thiserror::Error; @@ -214,6 +214,66 @@ bitflags! { } } +#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ExecDlConfig { + preimage: Preimage, + signature_len: usize, +} + +impl ExecDlConfig { + pub fn new(preimage: Preimage, signature_len: usize) -> Self { + Self { + preimage, + signature_len, + } + } + + pub fn auth(&self) -> H160 { + self.preimage.auth() + } + + pub fn preimage(&self) -> &[u8] { + self.preimage.preimage() + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)] +pub struct Preimage { + preimage: Vec, +} + +impl Preimage { + pub fn new_with_exec(script: Script, place: u8, bounds: [u8; 8], pubkey_hash: H160) -> Self { + let mut preimage = Vec::with_capacity(32 + 1 + 1 + 8 + 20); + + preimage.put(script.code_hash().as_slice()); + preimage.put(script.hash_type().as_slice()); + preimage.put_u8(place); + preimage.put(bounds.as_slice()); + preimage.put(pubkey_hash.as_bytes()); + + Self { preimage } + } + + pub fn new_with_dl(script: Script, pubkey_hash: H160) -> Self { + let mut preimage = Vec::with_capacity(32 + 1 + 20); + + preimage.put(script.code_hash().as_slice()); + preimage.put(script.hash_type().as_slice()); + preimage.put(pubkey_hash.as_bytes()); + + Self { preimage } + } + + pub fn auth(&self) -> H160 { + blake160(&self.preimage) + } + + pub fn preimage(&self) -> &[u8] { + &self.preimage + } +} + impl Serialize for SmtProofEntryVec { fn serialize(&self, serializer: S) -> Result where @@ -461,11 +521,7 @@ pub struct OmniLockConfig { pub(crate) btc_sign_vtype: BTCSignVtype, - pub(crate) use_rsa: bool, - - pub(crate) use_iso9796_2: bool, - - pub(crate) rsa_pubkey: Option>, + pub(crate) exec_dl_config: Option, } impl OmniLockConfig { @@ -522,9 +578,7 @@ impl OmniLockConfig { enable_cobuild: false, cobuild_message: Some(Message::default().as_bytes()), btc_sign_vtype: BTCSignVtype::P2PKHUncompressed, - use_rsa: false, - use_iso9796_2: false, - rsa_pubkey: None, + exec_dl_config: None, }) } @@ -548,9 +602,7 @@ impl OmniLockConfig { enable_cobuild: false, cobuild_message: Some(Message::default().as_bytes()), btc_sign_vtype: BTCSignVtype::P2PKHUncompressed, - use_rsa: false, - use_iso9796_2: false, - rsa_pubkey: None, + exec_dl_config: None, } } @@ -598,15 +650,6 @@ impl OmniLockConfig { Self::new(IdentityFlag::Solana, blake160(pubkey.as_bytes())) } - pub fn new_rsa_from_pubkey(pubkey: &PKey) -> Self { - let mut c = Self::new( - IdentityFlag::Dl, - blake160(&pubkey.public_key_to_pem().unwrap()), - ); - c.rsa_pubkey = Some(pubkey.public_key_to_pem().unwrap()); - c - } - /// Create an ownerlock omnilock with according script hash. /// # Arguments /// * `script_hash` the proper blake160 hash of according ownerlock script. @@ -614,22 +657,22 @@ impl OmniLockConfig { Self::new(IdentityFlag::OwnerLock, script_hash) } - /// Create a new OmniLockConfig - pub fn new(flag: IdentityFlag, hash: H160) -> Self { - let auth_content = match flag { - IdentityFlag::PubkeyHash - | IdentityFlag::Ethereum - | IdentityFlag::EthereumDisplaying - | IdentityFlag::Bitcoin - | IdentityFlag::Dogecoin - | IdentityFlag::Eos - | IdentityFlag::Tron - | IdentityFlag::Solana - | IdentityFlag::OwnerLock - | IdentityFlag::Dl => hash, - _ => H160::from_slice(&[0; 20]).unwrap(), - }; + pub fn new_with_exec_preimage(config: ExecDlConfig) -> Self { + assert_eq!(config.preimage().len(), 32 + 1 + 1 + 8 + 20); + let mut c = Self::new(IdentityFlag::Exec, config.auth()); + c.exec_dl_config = Some(config); + c + } + + pub fn new_with_dl_preimage(config: ExecDlConfig) -> Self { + assert_eq!(config.preimage().len(), 32 + 1 + 20); + let mut c = Self::new(IdentityFlag::Dl, config.auth()); + c.exec_dl_config = Some(config); + c + } + /// Create a new OmniLockConfig + pub fn new(flag: IdentityFlag, auth_content: H160) -> Self { OmniLockConfig { id: Identity { flag, auth_content }, multisig_config: None, @@ -641,9 +684,7 @@ impl OmniLockConfig { enable_cobuild: false, cobuild_message: Some(Message::default().as_bytes()), btc_sign_vtype: BTCSignVtype::P2PKHUncompressed, - use_rsa: false, - use_iso9796_2: false, - rsa_pubkey: None, + exec_dl_config: None, } } @@ -847,7 +888,21 @@ impl OmniLockConfig { // IdentityFlag::OwnerLock => OmniLockWitnessLock::new_builder(), IdentityFlag::Solana => OmniLockWitnessLock::new_builder() .signature(Some(Bytes::from(vec![0u8; 96])).pack()), - _ => todo!("to support other placeholder_witness_lock implementions"), + IdentityFlag::Dl | IdentityFlag::Exec => { + let sig_len = self + .exec_dl_config + .as_ref() + .map(|c| c.signature_len) + .unwrap_or(65); + OmniLockWitnessLock::new_builder() + .signature(Some(Bytes::from(vec![0u8; sig_len])).pack()) + .preimage( + self.exec_dl_config + .as_ref() + .map(|i| Into::::into(i.preimage().to_vec())) + .pack(), + ) + } }; if unlock_mode == OmniUnlockMode::Admin { @@ -898,7 +953,10 @@ impl OmniLockConfig { let lock = self.placeholder_witness_lock(unlock_mode)?; Ok(WitnessArgs::new_builder().lock(Some(lock).pack()).build()) } - _ => todo!("to support other placeholder_witness implementions"), + IdentityFlag::Dl | IdentityFlag::Exec => { + let lock = self.placeholder_witness_lock(unlock_mode)?; + Ok(WitnessArgs::new_builder().lock(Some(lock).pack()).build()) + } } } diff --git a/src/unlock/signer.rs b/src/unlock/signer.rs index 06e139a8..dc204a34 100644 --- a/src/unlock/signer.rs +++ b/src/unlock/signer.rs @@ -17,10 +17,7 @@ use thiserror::Error; use crate::{ constants::{COMMON_PREFIX, MULTISIG_TYPE_HASH}, types::{cobuild::top_level::WitnessLayoutUnion, omni_lock::OmniLockWitnessLock}, - util::{ - btc_convert_message, btc_convert_sign, iso9796_2_signning_prepare_pubkey, - rsa_signning_prepare_pubkey, - }, + util::{btc_convert_message, btc_convert_sign}, }; use crate::{ traits::{Signer, SignerError, TransactionDependencyProvider}, @@ -754,7 +751,7 @@ impl ScriptSigner for OmniLockScriptSigner { if args.len() != self.config.get_args_len() { return false; } - + dbg!(1); if self.unlock_mode == OmniUnlockMode::Admin { if let Some(admin_config) = self.config.get_admin_config() { if args.len() < 54 { @@ -924,66 +921,9 @@ impl ScriptSigner for OmniLockScriptSigner { self.signer .sign(id.auth_content().as_ref(), msg.as_slice(), true, tx)? } - IdentityFlag::Dl => { - let (sig, _pubkey) = if self.config.use_rsa { - ( - self.signer.sign( - self.config - .rsa_pubkey - .as_ref() - .expect("rsa pubkey must exist"), - &message, - true, - tx, - )?, - { - let pubkey = openssl::pkey::PKey::public_key_from_pem( - self.config - .rsa_pubkey - .as_ref() - .expect("rsa pubkey must exist"), - ) - .unwrap(); - rsa_signning_prepare_pubkey(&pubkey) - }, - ) - } else if self.config.use_iso9796_2 { - ( - self.signer.sign( - self.config - .rsa_pubkey - .as_ref() - .expect("rsa pubkey must exist"), - &message, - true, - tx, - )?, - { - let pubkey = openssl::pkey::PKey::public_key_from_pem( - self.config - .rsa_pubkey - .as_ref() - .expect("rsa pubkey must exist"), - ) - .unwrap(); - iso9796_2_signning_prepare_pubkey(&pubkey) - }, - ) - } else { - (Default::default(), Default::default()) - }; - - // Question? - // - // let hash = blake160(pubkey.as_ref()); - // let preimage = gen_exec_preimage(&self.config.rsa_script, &hash); - // preimage_hash = blake160(preimage.as_ref()); - // write_back_preimage_hash(dummy, IDENTITY_FLAGS_DL, preimage_hash); - - sig - } - _ => { - todo!("not supported yet"); + IdentityFlag::Dl | IdentityFlag::Exec => { + self.signer + .sign(id.auth_content().as_ref(), &message, true, tx)? } }; if self.config.enable_cobuild { @@ -1014,7 +954,10 @@ impl ScriptSigner for OmniLockScriptSigner { matches!(self.unlock_mode, OmniUnlockMode::Admin), &self.config.build_proofs(), &id.to_auth(), - None, + self.config + .exec_dl_config + .clone() + .map(|i| i.preimage().to_vec().into()), )?; match &self.config.cobuild_message { Some(msg) => { @@ -1052,7 +995,10 @@ impl ScriptSigner for OmniLockScriptSigner { matches!(self.unlock_mode, OmniUnlockMode::Admin), &self.config.build_proofs(), &id.to_auth(), - None, + self.config + .exec_dl_config + .clone() + .map(|i| i.preimage().to_vec().into()), )?; current_witness = current_witness.as_builder().lock(Some(lock).pack()).build(); witnesses[witness_idx] = current_witness.as_bytes().pack(); diff --git a/src/util.rs b/src/util.rs index 38923ff9..2f171ecd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -6,15 +6,10 @@ use ckb_dao_utils::extract_dao_data; use ckb_hash::blake2b_256; use ckb_types::{ core::{Capacity, EpochNumber, EpochNumberWithFraction, HeaderView}, - packed::{CellOutput, Script}, + packed::CellOutput, prelude::*, H160, H256, U256, }; -use openssl::{ - hash::MessageDigest, - pkey::{PKey, Private, Public}, - sign::Signer, -}; use sha3::{Digest, Keccak256}; use crate::traits::LiveCell; @@ -261,96 +256,6 @@ pub fn hex_encode(message: &[u8]) -> String { String::from_utf8(res).unwrap() } -pub fn rsa_signning_prepare_pubkey(pubkey: &PKey) -> Vec { - let mut sig = vec![ - 1, // algorithm id - 1, // key size, 1024 - 0, // padding, PKCS# 1.5 - 6, // hash type SHA256 - ]; - - let pubkey2 = pubkey.rsa().unwrap(); - let mut e = pubkey2.e().to_vec(); - let mut n = pubkey2.n().to_vec(); - e.reverse(); - n.reverse(); - - while e.len() < 4 { - e.push(0); - } - while n.len() < 128 { - n.push(0); - } - sig.append(&mut e); // 4 bytes E - sig.append(&mut n); // N - - sig -} - -pub fn rsa_sign(msg: &[u8], key: &PKey) -> Vec { - let pem: Vec = key.public_key_to_pem().unwrap(); - let pubkey = PKey::public_key_from_pem(&pem).unwrap(); - - let mut sig = rsa_signning_prepare_pubkey(&pubkey); - - let mut signer = Signer::new(MessageDigest::sha256(), key).unwrap(); - signer.update(msg).unwrap(); - sig.extend(signer.sign_to_vec().unwrap()); // sig - - sig -} - -pub fn iso9796_2_signning_prepare_pubkey(pubkey: &PKey) -> Vec { - let mut sig = vec![ - 3, // algorithm id, CKB_VERIFY_ISO9796_2_BATCH - 1, // key size, 1024 - 0, // padding, PKCS# 1.5 - 6, // hash type SHA256 - ]; - - let pubkey2 = pubkey.rsa().unwrap(); - let mut e = pubkey2.e().to_vec(); - let mut n = pubkey2.n().to_vec(); - e.reverse(); - n.reverse(); - - while e.len() < 4 { - e.push(0); - } - while n.len() < 128 { - n.push(0); - } - sig.append(&mut e); // 4 bytes E - sig.append(&mut n); // N - - sig -} - -pub fn iso9796_2_batch_sign(msg: &[u8], key: &PKey) -> Vec { - let pem: Vec = key.public_key_to_pem().unwrap(); - let pubkey = PKey::public_key_from_pem(&pem).unwrap(); - - let mut sig = iso9796_2_signning_prepare_pubkey(&pubkey); - - let mut signer = Signer::new(MessageDigest::sha256(), key).unwrap(); - signer.update(msg).unwrap(); - sig.extend(signer.sign_to_vec().unwrap()); // sig - sig.extend(signer.sign_to_vec().unwrap()); // sig - sig.extend(signer.sign_to_vec().unwrap()); // sig - sig.extend(signer.sign_to_vec().unwrap()); // sig - - sig -} - -pub fn gen_exec_preimage(script: &Script, blake160: &Bytes) -> Bytes { - let mut result = BytesMut::new(); - result.put_slice(script.code_hash().as_slice()); - result.put_slice(script.hash_type().as_slice()); - result.put_slice(blake160.clone().as_ref()); - - result.freeze() -} - #[cfg(test)] mod tests { use super::*;