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
59pub trait ScriptSigner {
64 fn match_args(&self, args: &[u8]) -> bool;
65
66 fn sign_tx(
68 &self,
69 tx: &TransactionView,
70 script_group: &ScriptGroup,
71 ) -> Result<TransactionView, ScriptSignError>;
72}
73
74pub struct SecpSighashScriptSigner {
76 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 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(¶ms_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
286pub struct SecpMultisigScriptSigner {
288 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 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 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 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
473pub 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 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 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#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default)]
542pub enum OmniUnlockMode {
543 #[default]
545 Normal = 1,
546 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 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 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 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 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 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 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 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 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 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}