ckb_sdk/unlock/
signer.rs

1use std::{collections::HashSet, convert::TryFrom};
2
3use anyhow::anyhow;
4use ckb_hash::{blake2b_256, new_blake2b};
5use ckb_jsonrpc_types::ScriptHashType;
6use ckb_types::{
7    bytes::{Bytes, BytesMut},
8    core::TransactionView,
9    error::VerificationError,
10    packed::{self, BytesOpt, Script, WitnessArgs},
11    prelude::*,
12    H160, H256,
13};
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16
17use crate::{constants::MultisigScript, types::omni_lock::OmniLockWitnessLock};
18use crate::{
19    traits::{Signer, SignerError},
20    util::convert_keccak256_hash,
21};
22use crate::{
23    types::{AddressPayload, ScriptGroup, Since},
24    Address, NetworkType,
25};
26
27use super::{
28    omni_lock::{ConfigError, Identity},
29    IdentityFlag, OmniLockConfig,
30};
31
32#[derive(Error, Debug)]
33pub enum ScriptSignError {
34    #[error("signer error: `{0}`")]
35    Signer(#[from] SignerError),
36
37    #[error("witness count in current transaction not enough to cover current script group")]
38    WitnessNotEnough,
39
40    #[error("the witness is not empty and not WitnessArgs format: `{0}`")]
41    InvalidWitnessArgs(#[from] VerificationError),
42
43    #[error("the Omni lock witness lock field is invalid: `{0}`")]
44    InvalidOmniLockWitnessLock(String),
45
46    #[error("invalid multisig config: `{0}`")]
47    InvalidMultisigConfig(String),
48
49    #[error("there already too many signatures in current WitnessArgs.lock field (old_count + new_count > threshold)")]
50    TooManySignatures,
51
52    #[error("there is an configuration error: `{0}`")]
53    InvalidConfig(#[from] ConfigError),
54
55    #[error(transparent)]
56    Other(#[from] anyhow::Error),
57}
58
59/// Script signer logic:
60///   * Generate message to sign
61///   * Sign the message by wallet
62///   * Put the signature into tx.witnesses
63pub trait ScriptSigner {
64    fn match_args(&self, args: &[u8]) -> bool;
65
66    /// Add signature information to witnesses
67    fn sign_tx(
68        &self,
69        tx: &TransactionView,
70        script_group: &ScriptGroup,
71    ) -> Result<TransactionView, ScriptSignError>;
72}
73
74/// Signer for secp256k1 sighash all lock script
75pub struct SecpSighashScriptSigner {
76    // Can be: SecpCkbRawKeySigner, HardwareWalletSigner
77    signer: Box<dyn Signer>,
78}
79
80impl SecpSighashScriptSigner {
81    pub fn new(signer: Box<dyn Signer>) -> SecpSighashScriptSigner {
82        SecpSighashScriptSigner { signer }
83    }
84
85    pub fn signer(&self) -> &dyn Signer {
86        self.signer.as_ref()
87    }
88
89    fn sign_tx_with_owner_id(
90        &self,
91        owner_id: &[u8],
92        tx: &TransactionView,
93        script_group: &ScriptGroup,
94    ) -> Result<TransactionView, ScriptSignError> {
95        let witness_idx = script_group.input_indices[0];
96        let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
97        while witnesses.len() <= witness_idx {
98            witnesses.push(Default::default());
99        }
100        let tx_new = tx
101            .as_advanced_builder()
102            .set_witnesses(witnesses.clone())
103            .build();
104
105        let zero_lock = Bytes::from(vec![0u8; 65]);
106        let message = generate_message(&tx_new, script_group, zero_lock)?;
107
108        let signature = self.signer.sign(owner_id, message.as_ref(), true, tx)?;
109
110        // Put signature into witness
111        let witness_data = witnesses[witness_idx].raw_data();
112        let mut current_witness: WitnessArgs = if witness_data.is_empty() {
113            WitnessArgs::default()
114        } else {
115            WitnessArgs::from_slice(witness_data.as_ref())?
116        };
117        current_witness = current_witness
118            .as_builder()
119            .lock(Some(signature).pack())
120            .build();
121        witnesses[witness_idx] = current_witness.as_bytes().pack();
122        Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
123    }
124}
125
126impl ScriptSigner for SecpSighashScriptSigner {
127    fn match_args(&self, args: &[u8]) -> bool {
128        args.len() == 20 && self.signer.match_id(args)
129    }
130
131    fn sign_tx(
132        &self,
133        tx: &TransactionView,
134        script_group: &ScriptGroup,
135    ) -> Result<TransactionView, ScriptSignError> {
136        let args = script_group.script.args().raw_data();
137        self.sign_tx_with_owner_id(args.as_ref(), tx, script_group)
138    }
139}
140
141#[derive(Eq, PartialEq, Clone, Hash, Serialize, Deserialize, Debug)]
142pub struct MultisigConfig {
143    lock_code_hash: H256,
144    lock_hash_type: ScriptHashType,
145    sighash_addresses: Vec<H160>,
146    require_first_n: u8,
147    threshold: u8,
148}
149impl MultisigConfig {
150    pub fn new_with(
151        multisig_script: MultisigScript,
152        sighash_addresses: Vec<H160>,
153        require_first_n: u8,
154        threshold: u8,
155    ) -> Result<MultisigConfig, ScriptSignError> {
156        let mut addr_set: HashSet<&H160> = HashSet::default();
157        for addr in &sighash_addresses {
158            if !addr_set.insert(addr) {
159                return Err(ScriptSignError::InvalidMultisigConfig(format!(
160                    "Duplicated address: {:?}",
161                    addr
162                )));
163            }
164        }
165        if threshold as usize > sighash_addresses.len() {
166            return Err(ScriptSignError::InvalidMultisigConfig(format!(
167                "Invalid threshold {} > {}",
168                threshold,
169                sighash_addresses.len()
170            )));
171        }
172        if require_first_n > threshold {
173            return Err(ScriptSignError::InvalidMultisigConfig(format!(
174                "Invalid require-first-n {} > {}",
175                require_first_n, threshold
176            )));
177        }
178        let multisig_script_id = multisig_script.script_id();
179        Ok(MultisigConfig {
180            lock_code_hash: multisig_script_id.code_hash,
181            lock_hash_type: multisig_script_id.hash_type.into(),
182            sighash_addresses,
183            require_first_n,
184            threshold,
185        })
186    }
187
188    pub fn contains_address(&self, target: &H160) -> bool {
189        self.sighash_addresses
190            .iter()
191            .any(|payload| payload == target)
192    }
193    pub fn sighash_addresses(&self) -> &Vec<H160> {
194        &self.sighash_addresses
195    }
196    pub fn lock_code_hash(&self) -> H256 {
197        self.lock_code_hash.clone()
198    }
199    pub fn lock_hash_type(&self) -> ScriptHashType {
200        self.lock_hash_type
201    }
202    pub fn require_first_n(&self) -> u8 {
203        self.require_first_n
204    }
205    pub fn threshold(&self) -> u8 {
206        self.threshold
207    }
208
209    pub fn hash160(&self) -> H160 {
210        let witness_data = self.to_witness_data();
211        let params_hash = blake2b_256(witness_data);
212        H160::from_slice(&params_hash[0..20]).unwrap()
213    }
214
215    pub fn to_address_payload(
216        &self,
217        multisig_script: MultisigScript,
218        since_absolute_epoch: Option<u64>,
219    ) -> AddressPayload {
220        let hash160 = self.hash160();
221
222        let mut args = BytesMut::from(hash160.as_bytes());
223        if let Some(absolute_epoch_number) = since_absolute_epoch {
224            let since_value = Since::new_absolute_epoch(absolute_epoch_number).value();
225            args.extend_from_slice(&since_value.to_le_bytes()[..]);
226        }
227        AddressPayload::new_full(
228            multisig_script.script_id().hash_type,
229            multisig_script.script_id().code_hash.pack(),
230            args.freeze(),
231        )
232    }
233
234    pub fn to_witness_data(&self) -> Vec<u8> {
235        let reserved_byte = 0u8;
236        let mut witness_data = vec![
237            reserved_byte,
238            self.require_first_n,
239            self.threshold,
240            self.sighash_addresses.len() as u8,
241        ];
242        for sighash_address in &self.sighash_addresses {
243            witness_data.extend_from_slice(sighash_address.as_bytes());
244        }
245        witness_data
246    }
247
248    pub fn placeholder_witness(&self) -> WitnessArgs {
249        let config_data = self.to_witness_data();
250        let mut zero_lock = vec![0u8; config_data.len() + 65 * self.threshold() as usize];
251        zero_lock[0..config_data.len()].copy_from_slice(config_data.as_ref());
252        WitnessArgs::new_builder()
253            .lock(Some(Bytes::from(zero_lock)).pack())
254            .build()
255    }
256
257    pub fn to_address(
258        &self,
259        network: NetworkType,
260        multisig_script: MultisigScript,
261        since_absolute_epoch: Option<u64>,
262    ) -> Address {
263        let payload = self.to_address_payload(multisig_script, since_absolute_epoch);
264        Address::new(network, payload, true)
265    }
266}
267
268impl From<&MultisigConfig> for Script {
269    fn from(value: &MultisigConfig) -> Self {
270        let multisig_script = MultisigScript::try_from(value.lock_code_hash.clone())
271            .unwrap_or_else(|err| {
272                panic!(
273                    "lock_code_hash {} is not multisig script code hash: {:?}",
274                    value.lock_code_hash, err
275                )
276            })
277            .script_id();
278        Script::new_builder()
279            .code_hash(multisig_script.code_hash.pack())
280            .hash_type(multisig_script.hash_type)
281            .args(Bytes::from(value.hash160().as_bytes().to_vec()).pack())
282            .build()
283    }
284}
285
286/// Signer for secp256k1 multisig all lock script
287pub struct SecpMultisigScriptSigner {
288    // Can be: SecpCkbRawKeySigner, HardwareWalletSigner
289    signer: Box<dyn Signer>,
290    config: MultisigConfig,
291    config_hash: [u8; 32],
292}
293impl SecpMultisigScriptSigner {
294    pub fn new(signer: Box<dyn Signer>, config: MultisigConfig) -> SecpMultisigScriptSigner {
295        let config_hash = blake2b_256(config.to_witness_data());
296        SecpMultisigScriptSigner {
297            signer,
298            config,
299            config_hash,
300        }
301    }
302    pub fn signer(&self) -> &dyn Signer {
303        self.signer.as_ref()
304    }
305    pub fn config(&self) -> &MultisigConfig {
306        &self.config
307    }
308}
309
310impl ScriptSigner for SecpMultisigScriptSigner {
311    fn match_args(&self, args: &[u8]) -> bool {
312        self.config_hash[0..20] == args[0..20]
313            && self
314                .config
315                .sighash_addresses
316                .iter()
317                .any(|id| self.signer.match_id(id.as_bytes()))
318    }
319
320    fn sign_tx(
321        &self,
322        tx: &TransactionView,
323        script_group: &ScriptGroup,
324    ) -> Result<TransactionView, ScriptSignError> {
325        let witness_idx = script_group.input_indices[0];
326        let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
327        while witnesses.len() <= witness_idx {
328            witnesses.push(Default::default());
329        }
330        let tx_new = tx
331            .as_advanced_builder()
332            .set_witnesses(witnesses.clone())
333            .build();
334
335        let config_data = self.config.to_witness_data();
336        let mut zero_lock = vec![0u8; config_data.len() + 65 * (self.config.threshold as usize)];
337        zero_lock[0..config_data.len()].copy_from_slice(&config_data);
338        let message = generate_message(&tx_new, script_group, Bytes::from(zero_lock.clone()))?;
339
340        let signatures = self
341            .config
342            .sighash_addresses
343            .iter()
344            .filter(|id| self.signer.match_id(id.as_bytes()))
345            .map(|id| self.signer.sign(id.as_bytes(), message.as_ref(), true, tx))
346            .collect::<Result<Vec<_>, SignerError>>()?;
347        // Put signature into witness
348        let witness_idx = script_group.input_indices[0];
349        let witness_data = witnesses[witness_idx].raw_data();
350        let mut current_witness: WitnessArgs = if witness_data.is_empty() {
351            WitnessArgs::default()
352        } else {
353            WitnessArgs::from_slice(witness_data.as_ref())?
354        };
355        let mut lock_field = current_witness
356            .lock()
357            .to_opt()
358            .map(|data| data.raw_data().as_ref().to_vec())
359            .unwrap_or(zero_lock);
360        if lock_field.len() != config_data.len() + self.config.threshold() as usize * 65 {
361            return Err(ScriptSignError::Other(anyhow!(
362                "invalid witness lock field length: {}, expected: {}",
363                lock_field.len(),
364                config_data.len() + self.config.threshold() as usize * 65,
365            )));
366        }
367        for signature in signatures {
368            let mut idx = config_data.len();
369            while idx < lock_field.len() {
370                // Put signature into an empty place.
371                if lock_field[idx..idx + 65] == signature {
372                    break;
373                } else if lock_field[idx..idx + 65] == [0u8; 65] {
374                    lock_field[idx..idx + 65].copy_from_slice(signature.as_ref());
375                    break;
376                }
377                idx += 65;
378            }
379            if idx >= lock_field.len() {
380                return Err(ScriptSignError::TooManySignatures);
381            }
382        }
383
384        current_witness = current_witness
385            .as_builder()
386            .lock(Some(Bytes::from(lock_field)).pack())
387            .build();
388        witnesses[witness_idx] = current_witness.as_bytes().pack();
389        Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
390    }
391}
392
393pub struct AcpScriptSigner {
394    sighash_signer: SecpSighashScriptSigner,
395}
396
397impl AcpScriptSigner {
398    pub fn new(signer: Box<dyn Signer>) -> AcpScriptSigner {
399        let sighash_signer = SecpSighashScriptSigner::new(signer);
400        AcpScriptSigner { sighash_signer }
401    }
402}
403
404impl ScriptSigner for AcpScriptSigner {
405    fn match_args(&self, args: &[u8]) -> bool {
406        args.len() >= 20 && args.len() <= 22 && {
407            let id = &args[0..20];
408            self.sighash_signer.signer().match_id(id)
409        }
410    }
411
412    fn sign_tx(
413        &self,
414        tx: &TransactionView,
415        script_group: &ScriptGroup,
416    ) -> Result<TransactionView, ScriptSignError> {
417        let args = script_group.script.args().raw_data();
418        let id = &args[0..20];
419        self.sighash_signer
420            .sign_tx_with_owner_id(id, tx, script_group)
421    }
422}
423
424#[derive(Clone, Copy, Eq, PartialEq, Debug)]
425pub enum ChequeAction {
426    Claim,
427    Withdraw,
428}
429pub struct ChequeScriptSigner {
430    sighash_signer: SecpSighashScriptSigner,
431    action: ChequeAction,
432}
433impl ChequeScriptSigner {
434    pub fn new(signer: Box<dyn Signer>, action: ChequeAction) -> ChequeScriptSigner {
435        let sighash_signer = SecpSighashScriptSigner::new(signer);
436        ChequeScriptSigner {
437            sighash_signer,
438            action,
439        }
440    }
441    pub fn owner_id<'t>(&self, args: &'t [u8]) -> &'t [u8] {
442        if args.len() != 40 {
443            &args[0..0]
444        } else if self.action == ChequeAction::Claim {
445            &args[0..20]
446        } else {
447            &args[20..40]
448        }
449    }
450    pub fn action(&self) -> ChequeAction {
451        self.action
452    }
453}
454
455impl ScriptSigner for ChequeScriptSigner {
456    fn match_args(&self, args: &[u8]) -> bool {
457        // NOTE: Require signer raw key map as: {script_hash[0..20] -> private key}
458        args.len() == 40 && self.sighash_signer.signer().match_id(self.owner_id(args))
459    }
460
461    fn sign_tx(
462        &self,
463        tx: &TransactionView,
464        script_group: &ScriptGroup,
465    ) -> Result<TransactionView, ScriptSignError> {
466        let args = script_group.script.args().raw_data();
467        let id = self.owner_id(args.as_ref());
468        self.sighash_signer
469            .sign_tx_with_owner_id(id, tx, script_group)
470    }
471}
472
473/// Common logic of generate message for certain script group. Overwrite
474/// this method to support special use case.
475pub fn generate_message(
476    tx: &TransactionView,
477    script_group: &ScriptGroup,
478    zero_lock: Bytes,
479) -> Result<Bytes, ScriptSignError> {
480    if tx.witnesses().item_count() <= script_group.input_indices[0] {
481        return Err(ScriptSignError::WitnessNotEnough);
482    }
483
484    let witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
485    let witness_data = witnesses[script_group.input_indices[0]].raw_data();
486    let mut init_witness = if witness_data.is_empty() {
487        WitnessArgs::default()
488    } else {
489        WitnessArgs::from_slice(witness_data.as_ref())?
490    };
491    init_witness = init_witness
492        .as_builder()
493        .lock(Some(zero_lock).pack())
494        .build();
495    // Other witnesses in current script group
496    let other_witnesses: Vec<([u8; 8], Bytes)> = script_group
497        .input_indices
498        .iter()
499        .skip(1)
500        .filter_map(|idx| witnesses.get(*idx))
501        .map(|witness| {
502            (
503                (witness.item_count() as u64).to_le_bytes(),
504                witness.raw_data(),
505            )
506        })
507        .collect();
508    // The witnesses not covered by any inputs
509    let outter_witnesses: Vec<([u8; 8], Bytes)> = if tx.inputs().len() < witnesses.len() {
510        witnesses[tx.inputs().len()..witnesses.len()]
511            .iter()
512            .map(|witness| {
513                (
514                    (witness.item_count() as u64).to_le_bytes(),
515                    witness.raw_data(),
516                )
517            })
518            .collect()
519    } else {
520        Default::default()
521    };
522
523    let mut blake2b = new_blake2b();
524    blake2b.update(tx.hash().as_slice());
525    blake2b.update(&(init_witness.as_bytes().len() as u64).to_le_bytes());
526    blake2b.update(&init_witness.as_bytes());
527    for (len_le, data) in other_witnesses {
528        blake2b.update(&len_le);
529        blake2b.update(&data);
530    }
531    for (len_le, data) in outter_witnesses {
532        blake2b.update(&len_le);
533        blake2b.update(&data);
534    }
535    let mut message = vec![0u8; 32];
536    blake2b.finalize(&mut message);
537    Ok(Bytes::from(message))
538}
539
540/// specify the unlock mode for a omnilock transaction.
541#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default)]
542pub enum OmniUnlockMode {
543    /// Use the normal mode to unlock the omnilock transaction.
544    #[default]
545    Normal = 1,
546    /// Use the admin mode to unlock the omnilock transaction.
547    Admin = 2,
548}
549
550pub struct OmniLockScriptSigner {
551    signer: Box<dyn Signer>,
552    config: OmniLockConfig,
553    unlock_mode: OmniUnlockMode,
554}
555
556impl OmniLockScriptSigner {
557    pub fn new(
558        signer: Box<dyn Signer>,
559        config: OmniLockConfig,
560        unlock_mode: OmniUnlockMode,
561    ) -> OmniLockScriptSigner {
562        OmniLockScriptSigner {
563            signer,
564            config,
565            unlock_mode,
566        }
567    }
568    pub fn signer(&self) -> &dyn Signer {
569        self.signer.as_ref()
570    }
571    pub fn config(&self) -> &OmniLockConfig {
572        &self.config
573    }
574    /// return the unlock mode
575    pub fn unlock_mode(&self) -> OmniUnlockMode {
576        self.unlock_mode
577    }
578
579    fn sign_multisig_tx(
580        &self,
581        tx: &TransactionView,
582        script_group: &ScriptGroup,
583    ) -> Result<TransactionView, ScriptSignError> {
584        let witness_idx = script_group.input_indices[0];
585        let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
586        while witnesses.len() <= witness_idx {
587            witnesses.push(Default::default());
588        }
589        let tx_new = tx
590            .as_advanced_builder()
591            .set_witnesses(witnesses.clone())
592            .build();
593
594        let zero_lock = self.config.zero_lock(self.unlock_mode)?;
595        let zero_lock_len = zero_lock.len();
596        let message = generate_message(&tx_new, script_group, zero_lock)?;
597
598        let multisig_config = match self.unlock_mode {
599            OmniUnlockMode::Admin => self
600                .config
601                .get_admin_config()
602                .ok_or(ConfigError::NoAdminConfig)?
603                .get_multisig_config(),
604            OmniUnlockMode::Normal => self.config.multisig_config(),
605        }
606        .ok_or(ConfigError::NoMultiSigConfig)?;
607        let signatures = multisig_config
608            .sighash_addresses
609            .iter()
610            .filter(|id| self.signer.match_id(id.as_bytes()))
611            .map(|id| self.signer.sign(id.as_bytes(), message.as_ref(), true, tx))
612            .collect::<Result<Vec<_>, SignerError>>()?;
613        // Put signature into witness
614        let witness_idx = script_group.input_indices[0];
615        let witness_data = witnesses[witness_idx].raw_data();
616        let mut current_witness: WitnessArgs = if witness_data.is_empty() {
617            WitnessArgs::default()
618        } else {
619            WitnessArgs::from_slice(witness_data.as_ref())?
620        };
621        let lock_field = current_witness.lock().to_opt().map(|data| data.raw_data());
622        let omnilock_witnesslock = if let Some(lock_field) = lock_field {
623            if lock_field.len() != zero_lock_len {
624                return Err(ScriptSignError::Other(anyhow!(
625                    "invalid witness lock field length: {}, expected: {}",
626                    lock_field.len(),
627                    zero_lock_len,
628                )));
629            }
630            OmniLockWitnessLock::from_slice(lock_field.as_ref())?
631        } else {
632            OmniLockWitnessLock::default()
633        };
634        let config_data = multisig_config.to_witness_data();
635        let mut omni_sig = omnilock_witnesslock
636            .signature()
637            .to_opt()
638            .map(|data| data.raw_data().as_ref().to_vec())
639            .unwrap_or_else(|| {
640                let mut omni_sig =
641                    vec![0u8; config_data.len() + multisig_config.threshold() as usize * 65];
642                omni_sig[..config_data.len()].copy_from_slice(&config_data);
643                omni_sig
644            });
645        for signature in signatures {
646            let mut idx = config_data.len();
647            while idx < omni_sig.len() {
648                // Put signature into an empty place.
649                if omni_sig[idx..idx + 65] == signature {
650                    break;
651                } else if omni_sig[idx..idx + 65] == [0u8; 65] {
652                    omni_sig[idx..idx + 65].copy_from_slice(signature.as_ref());
653                    break;
654                }
655                idx += 65;
656            }
657            if idx >= omni_sig.len() {
658                return Err(ScriptSignError::TooManySignatures);
659            }
660        }
661        let lock = omnilock_witnesslock
662            .as_builder()
663            .signature(Some(Bytes::from(omni_sig)).pack())
664            .build()
665            .as_bytes();
666
667        current_witness = current_witness.as_builder().lock(Some(lock).pack()).build();
668        witnesses[witness_idx] = current_witness.as_bytes().pack();
669        Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
670    }
671
672    fn sign_ethereum_tx(
673        &self,
674        tx: &TransactionView,
675        script_group: &ScriptGroup,
676        id: &Identity,
677    ) -> Result<TransactionView, ScriptSignError> {
678        let witness_idx = script_group.input_indices[0];
679        let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
680        while witnesses.len() <= witness_idx {
681            witnesses.push(Default::default());
682        }
683        let tx_new = tx
684            .as_advanced_builder()
685            .set_witnesses(witnesses.clone())
686            .build();
687
688        let zero_lock = self.config.zero_lock(self.unlock_mode())?;
689        let message = generate_message(&tx_new, script_group, zero_lock)?;
690        let message = convert_keccak256_hash(message.as_ref());
691
692        let signature = self
693            .signer
694            .sign(id.auth_content().as_ref(), message.as_ref(), true, tx)?;
695
696        // Put signature into witness
697        let witness_data = witnesses[witness_idx].raw_data();
698        let mut current_witness: WitnessArgs = if witness_data.is_empty() {
699            WitnessArgs::default()
700        } else {
701            WitnessArgs::from_slice(witness_data.as_ref())?
702        };
703
704        let lock = Self::build_witness_lock(current_witness.lock(), signature)?;
705        current_witness = current_witness.as_builder().lock(Some(lock).pack()).build();
706        witnesses[witness_idx] = current_witness.as_bytes().pack();
707        Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
708    }
709
710    /// Build proper witness lock
711    pub fn build_witness_lock(
712        orig_lock: BytesOpt,
713        signature: Bytes,
714    ) -> Result<Bytes, ScriptSignError> {
715        let lock_field = orig_lock.to_opt().map(|data| data.raw_data());
716        let omnilock_witnesslock = if let Some(lock_field) = lock_field {
717            OmniLockWitnessLock::from_slice(lock_field.as_ref())?
718        } else {
719            OmniLockWitnessLock::default()
720        };
721
722        Ok(omnilock_witnesslock
723            .as_builder()
724            .signature(Some(signature).pack())
725            .build()
726            .as_bytes())
727    }
728}
729
730impl ScriptSigner for OmniLockScriptSigner {
731    fn match_args(&self, args: &[u8]) -> bool {
732        if args.len() != self.config.get_args_len() {
733            return false;
734        }
735
736        if self.unlock_mode == OmniUnlockMode::Admin {
737            if let Some(admin_config) = self.config.get_admin_config() {
738                if args.len() < 54 {
739                    return false;
740                }
741                // Check if the args match the rc_type_id in the admin_config
742                if admin_config.rc_type_id().as_bytes() != &args[22..54] {
743                    return false;
744                }
745                if let Some(multisig_cfg) = admin_config.get_multisig_config() {
746                    return multisig_cfg
747                        .sighash_addresses
748                        .iter()
749                        .any(|id| self.signer.match_id(id.as_bytes()));
750                } else {
751                    return self
752                        .signer
753                        .match_id(admin_config.get_auth().auth_content().as_bytes());
754                }
755            }
756            return false;
757        }
758        if self.config.id().flag() as u8 != args[0] {
759            return false;
760        }
761        match self.config.id().flag() {
762            IdentityFlag::PubkeyHash | IdentityFlag::Ethereum => self
763                .signer
764                .match_id(self.config.id().auth_content().as_ref()),
765            IdentityFlag::Multisig => {
766                self.config.id().auth_content().as_ref() == &args[1..21]
767                    && self
768                        .config
769                        .multisig_config()
770                        .unwrap()
771                        .sighash_addresses
772                        .iter()
773                        .any(|id| self.signer.match_id(id.as_bytes()))
774            }
775
776            IdentityFlag::OwnerLock => {
777                // should not reach here, return true for compatible reason
778                true
779            }
780            _ => todo!("other auth type not supported yet"),
781        }
782    }
783
784    fn sign_tx(
785        &self,
786        tx: &TransactionView,
787        script_group: &ScriptGroup,
788    ) -> Result<TransactionView, ScriptSignError> {
789        let id = match self.unlock_mode {
790            OmniUnlockMode::Admin => self
791                .config
792                .get_admin_config()
793                .ok_or(ConfigError::NoAdminConfig)?
794                .get_auth()
795                .clone(),
796            OmniUnlockMode::Normal => self.config.id().clone(),
797        };
798        match id.flag() {
799            IdentityFlag::PubkeyHash => {
800                let witness_idx = script_group.input_indices[0];
801                let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
802                while witnesses.len() <= witness_idx {
803                    witnesses.push(Default::default());
804                }
805                let tx_new = tx
806                    .as_advanced_builder()
807                    .set_witnesses(witnesses.clone())
808                    .build();
809
810                let zero_lock = self.config.zero_lock(self.unlock_mode)?;
811                let message = generate_message(&tx_new, script_group, zero_lock)?;
812
813                let signature =
814                    self.signer
815                        .sign(id.auth_content().as_ref(), message.as_ref(), true, tx)?;
816
817                // Put signature into witness
818                let witness_data = witnesses[witness_idx].raw_data();
819                let mut current_witness: WitnessArgs = if witness_data.is_empty() {
820                    WitnessArgs::default()
821                } else {
822                    WitnessArgs::from_slice(witness_data.as_ref())?
823                };
824
825                let lock = Self::build_witness_lock(current_witness.lock(), signature)?;
826
827                current_witness = current_witness.as_builder().lock(Some(lock).pack()).build();
828                witnesses[witness_idx] = current_witness.as_bytes().pack();
829                Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
830            }
831            IdentityFlag::Ethereum => self.sign_ethereum_tx(tx, script_group, &id),
832            IdentityFlag::Multisig => self.sign_multisig_tx(tx, script_group),
833            IdentityFlag::OwnerLock => {
834                // should not reach here, just return a clone for compatible reason.
835                Ok(tx.clone())
836            }
837            _ => {
838                todo!("not supported yet");
839            }
840        }
841    }
842}
843
844#[cfg(test)]
845mod anyhow_tests {
846    use anyhow::anyhow;
847    #[test]
848    fn test_script_sign_error() {
849        let error = anyhow!(super::ScriptSignError::WitnessNotEnough);
850        assert_eq!(
851            "witness count in current transaction not enough to cover current script group",
852            error.to_string()
853        );
854    }
855}