1use anyhow::anyhow;
2use ckb_types::{
3 bytes::Bytes,
4 core::TransactionView,
5 packed::{self, Byte32, BytesOpt, WitnessArgs},
6 prelude::*,
7};
8use thiserror::Error;
9
10use super::{
11 omni_lock::{ConfigError, OmniLockFlags},
12 signer::{
13 AcpScriptSigner, ChequeAction, ChequeScriptSigner, MultisigConfig, ScriptSignError,
14 ScriptSigner, SecpMultisigScriptSigner, SecpSighashScriptSigner,
15 },
16 OmniLockConfig, OmniLockScriptSigner, OmniUnlockMode,
17};
18use crate::traits::{Signer, TransactionDependencyError, TransactionDependencyProvider};
19use crate::types::ScriptGroup;
20
21const CHEQUE_CLAIM_SINCE: u64 = 0;
22const CHEQUE_WITHDRAW_SINCE: u64 = 0xA000000000000006;
23
24#[derive(Error, Debug)]
25pub enum UnlockError {
26 #[error("sign script error: `{0}`")]
27 ScriptSigner(#[from] ScriptSignError),
28
29 #[error("transaction dependency error: `{0}`")]
30 TxDep(#[from] TransactionDependencyError),
31
32 #[error("invalid witness args: witness index=`{0}`")]
33 InvalidWitnessArgs(usize),
34
35 #[error("there is an configuration error: `{0}`")]
36 InvalidConfig(#[from] ConfigError),
37
38 #[error("sign context is incorrect")]
39 SignContextTypeIncorrect,
40
41 #[error(transparent)]
42 Other(#[from] anyhow::Error),
43}
44
45#[async_trait::async_trait]
52pub trait ScriptUnlocker: Sync + Send {
53 fn match_args(&self, args: &[u8]) -> bool;
54
55 async fn is_unlocked_async(
57 &self,
58 _tx: &TransactionView,
59 _script_group: &ScriptGroup,
60 _tx_dep_provider: &dyn TransactionDependencyProvider,
61 ) -> Result<bool, UnlockError> {
62 Ok(false)
63 }
64 #[cfg(not(target_arch = "wasm32"))]
65 fn is_unlocked(
66 &self,
67 tx: &TransactionView,
68 script_group: &ScriptGroup,
69 tx_dep_provider: &dyn TransactionDependencyProvider,
70 ) -> Result<bool, UnlockError> {
71 crate::rpc::block_on(self.is_unlocked_async(tx, script_group, tx_dep_provider))
72 }
73
74 async fn unlock_async(
77 &self,
78 tx: &TransactionView,
79 script_group: &ScriptGroup,
80 tx_dep_provider: &dyn TransactionDependencyProvider,
81 ) -> Result<TransactionView, UnlockError>;
82 #[cfg(not(target_arch = "wasm32"))]
83 fn unlock(
84 &self,
85 tx: &TransactionView,
86 script_group: &ScriptGroup,
87 tx_dep_provider: &dyn TransactionDependencyProvider,
88 ) -> Result<TransactionView, UnlockError> {
89 crate::rpc::block_on(self.unlock_async(tx, script_group, tx_dep_provider))
90 }
91
92 fn clear_placeholder_witness(
93 &self,
94 tx: &TransactionView,
95 _script_group: &ScriptGroup,
96 ) -> Result<TransactionView, UnlockError> {
97 Ok(tx.clone())
98 }
99
100 async fn fill_placeholder_witness_async(
102 &self,
103 tx: &TransactionView,
104 script_group: &ScriptGroup,
105 tx_dep_provider: &dyn TransactionDependencyProvider,
106 ) -> Result<TransactionView, UnlockError>;
107 #[cfg(not(target_arch = "wasm32"))]
108 fn fill_placeholder_witness(
109 &self,
110 tx: &TransactionView,
111 script_group: &ScriptGroup,
112 tx_dep_provider: &dyn TransactionDependencyProvider,
113 ) -> Result<TransactionView, UnlockError> {
114 crate::rpc::block_on(self.fill_placeholder_witness_async(tx, script_group, tx_dep_provider))
115 }
116}
117
118pub fn fill_witness_lock(
119 tx: &TransactionView,
120 script_group: &ScriptGroup,
121 lock_field: Bytes,
122) -> Result<TransactionView, UnlockError> {
123 let witness_idx = script_group.input_indices[0];
124 let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
125 while witnesses.len() <= witness_idx {
126 witnesses.push(Default::default());
127 }
128 let witness_data = witnesses[witness_idx].raw_data();
129 let mut witness = if witness_data.is_empty() {
130 WitnessArgs::default()
131 } else {
132 WitnessArgs::from_slice(witness_data.as_ref())
133 .map_err(|_| UnlockError::InvalidWitnessArgs(witness_idx))?
134 };
135 if witness.lock().is_none() {
136 witness = witness.as_builder().lock(Some(lock_field).pack()).build();
137 }
138 witnesses[witness_idx] = witness.as_bytes().pack();
139 Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
140}
141
142pub fn reset_witness_lock(
143 tx: TransactionView,
144 witness_idx: usize,
145) -> Result<TransactionView, usize> {
146 let mut witnesses: Vec<packed::Bytes> = tx.witnesses().into_iter().collect();
147 if let Some(witness_data) = witnesses
148 .get(witness_idx)
149 .map(|data| data.raw_data())
150 .filter(|data| !data.is_empty())
151 {
152 let witness = WitnessArgs::from_slice(witness_data.as_ref()).map_err(|_| witness_idx)?;
153 let data = if witness.input_type().is_none() && witness.output_type().is_none() {
154 Bytes::default()
155 } else {
156 witness
157 .as_builder()
158 .lock(BytesOpt::default())
159 .build()
160 .as_bytes()
161 };
162 witnesses[witness_idx] = data.pack();
163 Ok(tx.as_advanced_builder().set_witnesses(witnesses).build())
164 } else {
165 Ok(tx)
166 }
167}
168
169pub struct SecpSighashUnlocker {
170 signer: SecpSighashScriptSigner,
171}
172impl SecpSighashUnlocker {
173 pub fn new(signer: SecpSighashScriptSigner) -> SecpSighashUnlocker {
174 SecpSighashUnlocker { signer }
175 }
176}
177impl From<Box<dyn Signer>> for SecpSighashUnlocker {
178 fn from(signer: Box<dyn Signer>) -> SecpSighashUnlocker {
179 SecpSighashUnlocker::new(SecpSighashScriptSigner::new(signer))
180 }
181}
182#[async_trait::async_trait]
183impl ScriptUnlocker for SecpSighashUnlocker {
184 fn match_args(&self, args: &[u8]) -> bool {
185 self.signer.match_args(args)
186 }
187
188 async fn unlock_async(
189 &self,
190 tx: &TransactionView,
191 script_group: &ScriptGroup,
192 _tx_dep_provider: &dyn TransactionDependencyProvider,
193 ) -> Result<TransactionView, UnlockError> {
194 Ok(self.signer.sign_tx(tx, script_group)?)
195 }
196
197 async fn fill_placeholder_witness_async(
198 &self,
199 tx: &TransactionView,
200 script_group: &ScriptGroup,
201 _tx_dep_provider: &dyn TransactionDependencyProvider,
202 ) -> Result<TransactionView, UnlockError> {
203 fill_witness_lock(tx, script_group, Bytes::from(vec![0u8; 65]))
204 }
205}
206
207pub struct SecpMultisigUnlocker {
208 signer: SecpMultisigScriptSigner,
209}
210impl SecpMultisigUnlocker {
211 pub fn new(signer: SecpMultisigScriptSigner) -> SecpMultisigUnlocker {
212 SecpMultisigUnlocker { signer }
213 }
214}
215impl From<(Box<dyn Signer>, MultisigConfig)> for SecpMultisigUnlocker {
216 fn from((signer, config): (Box<dyn Signer>, MultisigConfig)) -> SecpMultisigUnlocker {
217 SecpMultisigUnlocker::new(SecpMultisigScriptSigner::new(signer, config))
218 }
219}
220#[async_trait::async_trait]
221impl ScriptUnlocker for SecpMultisigUnlocker {
222 fn match_args(&self, args: &[u8]) -> bool {
223 (args.len() == 20 || args.len() == 28) && self.signer.match_args(args)
224 }
225
226 async fn unlock_async(
227 &self,
228 tx: &TransactionView,
229 script_group: &ScriptGroup,
230 _tx_dep_provider: &dyn TransactionDependencyProvider,
231 ) -> Result<TransactionView, UnlockError> {
232 Ok(self.signer.sign_tx(tx, script_group)?)
233 }
234
235 async fn fill_placeholder_witness_async(
236 &self,
237 tx: &TransactionView,
238 script_group: &ScriptGroup,
239 _tx_dep_provider: &dyn TransactionDependencyProvider,
240 ) -> Result<TransactionView, UnlockError> {
241 let config = self.signer.config();
242 let config_data = config.to_witness_data();
243 let mut zero_lock = vec![0u8; config_data.len() + 65 * (config.threshold() as usize)];
244 zero_lock[0..config_data.len()].copy_from_slice(&config_data);
245 fill_witness_lock(tx, script_group, Bytes::from(zero_lock))
246 }
247}
248
249pub struct AcpUnlocker {
250 signer: AcpScriptSigner,
251}
252
253impl AcpUnlocker {
254 pub fn new(signer: AcpScriptSigner) -> AcpUnlocker {
255 AcpUnlocker { signer }
256 }
257}
258impl From<Box<dyn Signer>> for AcpUnlocker {
259 fn from(signer: Box<dyn Signer>) -> AcpUnlocker {
260 AcpUnlocker::new(AcpScriptSigner::new(signer))
261 }
262}
263
264async fn acp_is_unlocked(
265 tx: &TransactionView,
266 script_group: &ScriptGroup,
267 tx_dep_provider: &dyn TransactionDependencyProvider,
268 acp_args: &[u8],
269) -> Result<bool, UnlockError> {
270 const POW10: [u64; 20] = [
271 1,
272 10,
273 100,
274 1000,
275 10000,
276 100000,
277 1000000,
278 10000000,
279 100000000,
280 1000000000,
281 10000000000,
282 100000000000,
283 1000000000000,
284 10000000000000,
285 100000000000000,
286 1000000000000000,
287 10000000000000000,
288 100000000000000000,
289 1000000000000000000,
290 10000000000000000000,
291 ];
292 let min_ckb_amount = if acp_args.is_empty() {
293 0
294 } else {
295 let idx = acp_args[0];
296 if idx >= 20 {
297 return Err(UnlockError::Other(anyhow!("invalid min ckb amount config in script.args, got: {}, expected: value >=0 and value < 20", idx)));
298 }
299 POW10[idx as usize]
300 };
301 let min_udt_amount = if acp_args.len() > 1 {
302 let idx = acp_args[1];
303 if idx >= 39 {
304 return Err(UnlockError::Other(anyhow!("invalid min udt amount config in script.args, got: {}, expected: value >=0 and value < 39", idx)));
305 }
306 if idx >= 20 {
307 (POW10[19] as u128) * (POW10[idx as usize - 19] as u128)
308 } else {
309 POW10[idx as usize] as u128
310 }
311 } else {
312 0
313 };
314
315 struct InputWallet {
316 type_hash_opt: Option<Byte32>,
317 ckb_amount: u64,
318 udt_amount: u128,
319 output_cnt: usize,
320 }
321 let mut input_wallets = Vec::new();
322
323 for idx in script_group.input_indices.iter() {
324 let input = tx
325 .inputs()
326 .get(*idx)
327 .ok_or_else(|| anyhow!("input index in script group is out of bound: {}", idx))?;
328 let output = tx_dep_provider
329 .get_cell_async(&input.previous_output())
330 .await?;
331 let output_data = tx_dep_provider
332 .get_cell_data_async(&input.previous_output())
333 .await?;
334
335 let type_hash_opt = output
336 .type_()
337 .to_opt()
338 .map(|script| script.calc_script_hash());
339 if type_hash_opt.is_some() && output_data.len() < 16 {
340 return Err(UnlockError::Other(anyhow!(
341 "invalid udt output data in input cell: {:?}",
342 input
343 )));
344 }
345 let udt_amount = if type_hash_opt.is_some() {
346 let mut amount_bytes = [0u8; 16];
347 amount_bytes.copy_from_slice(&output_data[0..16]);
348 u128::from_le_bytes(amount_bytes)
349 } else {
350 0
351 };
352 input_wallets.push(InputWallet {
353 type_hash_opt,
354 ckb_amount: output.capacity().unpack(),
355 udt_amount,
356 output_cnt: 0,
357 })
358 }
359
360 for (output_idx, output) in tx.outputs().into_iter().enumerate() {
361 if output.lock() != script_group.script {
362 continue;
363 }
364 let output_data: Bytes = tx
365 .outputs_data()
366 .get(output_idx)
367 .map(|data| data.raw_data())
368 .ok_or_else(|| {
369 anyhow!(
370 "output data index in script group is out of bound: {}",
371 output_idx
372 )
373 })?;
374 let type_hash_opt = output
375 .type_()
376 .to_opt()
377 .map(|script| script.calc_script_hash());
378 if type_hash_opt.is_some() && output_data.len() < 16 {
379 return Err(UnlockError::Other(anyhow!(
380 "invalid udt output data in output cell: index={}",
381 output_idx
382 )));
383 }
384 let ckb_amount: u64 = output.capacity().unpack();
385 let udt_amount = if type_hash_opt.is_some() {
386 let mut amount_bytes = [0u8; 16];
387 amount_bytes.copy_from_slice(&output_data[0..16]);
388 u128::from_le_bytes(amount_bytes)
389 } else {
390 0
391 };
392 let mut found_inputs = 0;
393 for input_wallet in &mut input_wallets {
394 if input_wallet.type_hash_opt == type_hash_opt {
395 let (min_output_ckb_amount, ckb_overflow) =
396 input_wallet.ckb_amount.overflowing_add(min_ckb_amount);
397 let meet_ckb_cond = !ckb_overflow && ckb_amount >= min_output_ckb_amount;
398 let (min_output_udt_amount, udt_overflow) =
399 input_wallet.udt_amount.overflowing_add(min_udt_amount);
400 let meet_udt_cond = !udt_overflow && udt_amount >= min_output_udt_amount;
401 if !(meet_ckb_cond || meet_udt_cond) {
402 return Ok(false);
404 }
405 if (!meet_ckb_cond && ckb_amount != input_wallet.ckb_amount)
406 || (!meet_udt_cond && udt_amount != input_wallet.udt_amount)
407 {
408 return Ok(false);
410 }
411 found_inputs += 1;
412 input_wallet.output_cnt += 1;
413 if found_inputs > 1 {
414 return Ok(false);
416 }
417 if input_wallet.output_cnt > 1 {
418 return Ok(false);
420 }
421 }
422 }
423 if found_inputs != 1 {
424 return Ok(false);
426 }
427 }
428 for input_wallet in &input_wallets {
429 if input_wallet.output_cnt != 1 {
430 return Ok(false);
432 }
433 }
434 Ok(true)
435}
436
437#[async_trait::async_trait]
438impl ScriptUnlocker for AcpUnlocker {
439 fn match_args(&self, args: &[u8]) -> bool {
440 self.signer.match_args(args)
441 }
442
443 async fn is_unlocked_async(
444 &self,
445 tx: &TransactionView,
446 script_group: &ScriptGroup,
447 tx_dep_provider: &dyn TransactionDependencyProvider,
448 ) -> Result<bool, UnlockError> {
449 let raw_data = script_group.script.args().raw_data();
450 let acp_args = {
451 let data = raw_data.as_ref();
452 if data.len() > 20 {
453 &data[20..]
454 } else {
455 &[]
456 }
457 };
458 acp_is_unlocked(tx, script_group, tx_dep_provider, acp_args).await
459 }
460
461 async fn unlock_async(
462 &self,
463 tx: &TransactionView,
464 script_group: &ScriptGroup,
465 tx_dep_provider: &dyn TransactionDependencyProvider,
466 ) -> Result<TransactionView, UnlockError> {
467 if self
468 .is_unlocked_async(tx, script_group, tx_dep_provider)
469 .await?
470 {
471 self.clear_placeholder_witness(tx, script_group)
472 } else {
473 Ok(self.signer.sign_tx(tx, script_group)?)
474 }
475 }
476
477 fn clear_placeholder_witness(
478 &self,
479 tx: &TransactionView,
480 script_group: &ScriptGroup,
481 ) -> Result<TransactionView, UnlockError> {
482 reset_witness_lock(tx.clone(), script_group.input_indices[0])
483 .map_err(UnlockError::InvalidWitnessArgs)
484 }
485
486 async fn fill_placeholder_witness_async(
487 &self,
488 tx: &TransactionView,
489 script_group: &ScriptGroup,
490 tx_dep_provider: &dyn TransactionDependencyProvider,
491 ) -> Result<TransactionView, UnlockError> {
492 if self
493 .is_unlocked_async(tx, script_group, tx_dep_provider)
494 .await?
495 {
496 Ok(tx.clone())
497 } else {
498 fill_witness_lock(tx, script_group, Bytes::from(vec![0u8; 65]))
499 }
500 }
501}
502
503pub struct ChequeUnlocker {
504 signer: ChequeScriptSigner,
505}
506impl ChequeUnlocker {
507 pub fn new(signer: ChequeScriptSigner) -> ChequeUnlocker {
508 ChequeUnlocker { signer }
509 }
510}
511impl From<(Box<dyn Signer>, ChequeAction)> for ChequeUnlocker {
512 fn from((signer, action): (Box<dyn Signer>, ChequeAction)) -> ChequeUnlocker {
513 ChequeUnlocker::new(ChequeScriptSigner::new(signer, action))
514 }
515}
516
517#[async_trait::async_trait]
518impl ScriptUnlocker for ChequeUnlocker {
519 fn match_args(&self, args: &[u8]) -> bool {
520 self.signer.match_args(args)
521 }
522
523 async fn is_unlocked_async(
524 &self,
525 tx: &TransactionView,
526 script_group: &ScriptGroup,
527 tx_dep_provider: &dyn TransactionDependencyProvider,
528 ) -> Result<bool, UnlockError> {
529 let args = script_group.script.args().raw_data();
530 if args.len() != 40 {
531 return Err(UnlockError::Other(anyhow!(
532 "invalid script args length, expected: 40, got: {}",
533 args.len()
534 )));
535 }
536 let inputs: Vec<_> = tx.inputs().into_iter().collect();
537 let group_since_list: Vec<u64> = script_group
538 .input_indices
539 .iter()
540 .map(|idx| inputs[*idx].since().unpack())
541 .collect();
542
543 let receiver_lock_hash = &args.as_ref()[0..20];
545 let sender_lock_hash = &args.as_ref()[20..40];
546 let mut receiver_lock_witness = None;
547 let mut sender_lock_witness = None;
548 for (input_idx, input) in inputs.into_iter().enumerate() {
549 let output = tx_dep_provider
550 .get_cell_async(&input.previous_output())
551 .await?;
552 let lock_hash = output.lock().calc_script_hash();
553 let lock_hash_prefix = &lock_hash.as_slice()[0..20];
554 let witness = tx
555 .witnesses()
556 .get(input_idx)
557 .map(|witness| witness.raw_data())
558 .unwrap_or_default();
559
560 #[allow(clippy::collapsible_if)]
561 if lock_hash_prefix == receiver_lock_hash {
562 if receiver_lock_witness.is_none() {
563 receiver_lock_witness = Some((input_idx, witness));
564 }
565 } else if lock_hash_prefix == sender_lock_hash {
566 if sender_lock_witness.is_none() {
567 sender_lock_witness = Some((input_idx, witness));
568 }
569 }
570 }
571 if self.signer.action() == ChequeAction::Claim {
573 if let Some((_input_idx, witness)) = receiver_lock_witness {
574 if group_since_list
575 .iter()
576 .any(|since| *since != CHEQUE_CLAIM_SINCE)
577 {
578 return Err(UnlockError::Other(anyhow!(
579 "claim action must have all zero since in cheque inputs"
580 )));
581 }
582 let witness_args = match WitnessArgs::from_slice(witness.as_ref()) {
583 Ok(args) => args,
584 Err(_) => {
585 return Ok(false);
586 }
587 };
588 if witness_args.lock().to_opt().is_none() {
589 return Ok(false);
590 }
591 return Ok(true);
592 }
593 } else if let Some((_input_idx, witness)) = sender_lock_witness {
594 if group_since_list
595 .iter()
596 .any(|since| *since != CHEQUE_WITHDRAW_SINCE)
597 {
598 return Err(UnlockError::Other(anyhow!(
599 "withdraw action must have all relative 6 epochs since in cheque inputs"
600 )));
601 }
602 let witness_args = match WitnessArgs::from_slice(witness.as_ref()) {
603 Ok(args) => args,
604 Err(_) => {
605 return Ok(false);
606 }
607 };
608 if witness_args.lock().to_opt().is_none() {
609 return Ok(false);
610 }
611 return Ok(true);
612 }
613 Ok(false)
614 }
615
616 async fn unlock_async(
617 &self,
618 tx: &TransactionView,
619 script_group: &ScriptGroup,
620 tx_dep_provider: &dyn TransactionDependencyProvider,
621 ) -> Result<TransactionView, UnlockError> {
622 if self
623 .is_unlocked_async(tx, script_group, tx_dep_provider)
624 .await?
625 {
626 self.clear_placeholder_witness(tx, script_group)
627 } else {
628 Ok(self.signer.sign_tx(tx, script_group)?)
629 }
630 }
631
632 fn clear_placeholder_witness(
633 &self,
634 tx: &TransactionView,
635 script_group: &ScriptGroup,
636 ) -> Result<TransactionView, UnlockError> {
637 reset_witness_lock(tx.clone(), script_group.input_indices[0])
638 .map_err(UnlockError::InvalidWitnessArgs)
639 }
640
641 async fn fill_placeholder_witness_async(
642 &self,
643 tx: &TransactionView,
644 script_group: &ScriptGroup,
645 tx_dep_provider: &dyn TransactionDependencyProvider,
646 ) -> Result<TransactionView, UnlockError> {
647 if self
648 .is_unlocked_async(tx, script_group, tx_dep_provider)
649 .await?
650 {
651 Ok(tx.clone())
652 } else {
653 fill_witness_lock(tx, script_group, Bytes::from(vec![0u8; 65]))
654 }
655 }
656}
657
658pub struct OmniLockUnlocker {
659 signer: OmniLockScriptSigner,
660 config: OmniLockConfig,
661}
662impl OmniLockUnlocker {
663 pub fn new(signer: OmniLockScriptSigner, config: OmniLockConfig) -> OmniLockUnlocker {
664 OmniLockUnlocker { signer, config }
665 }
666}
667impl From<(Box<dyn Signer>, OmniLockConfig, OmniUnlockMode)> for OmniLockUnlocker {
668 fn from(
669 (signer, config, unlock_mode): (Box<dyn Signer>, OmniLockConfig, OmniUnlockMode),
670 ) -> OmniLockUnlocker {
671 let cfg = config.clone();
672 OmniLockUnlocker::new(OmniLockScriptSigner::new(signer, config, unlock_mode), cfg)
673 }
674}
675#[async_trait::async_trait]
676impl ScriptUnlocker for OmniLockUnlocker {
677 fn match_args(&self, args: &[u8]) -> bool {
678 self.signer.match_args(args)
679 }
680
681 async fn is_unlocked_async(
683 &self,
684 tx: &TransactionView,
685 script_group: &ScriptGroup,
686 tx_dep_provider: &dyn TransactionDependencyProvider,
687 ) -> Result<bool, UnlockError> {
688 if self.config.omni_lock_flags().contains(OmniLockFlags::ACP) {
689 let raw_data = script_group.script.args().raw_data();
690 let acp_args = {
691 let mut offset = 22;
692 if self.config.omni_lock_flags().contains(OmniLockFlags::ADMIN) {
693 offset += 32;
694 }
695 let data = raw_data.as_ref();
696 if data.len() > offset {
697 &data[offset..]
698 } else {
699 &[]
700 }
701 };
702 let acp_unlocked = acp_is_unlocked(tx, script_group, tx_dep_provider, acp_args).await?;
703 if acp_unlocked {
704 return Ok(true);
705 }
706 }
707 if !self.signer.config().is_ownerlock() {
708 return Ok(false);
709 }
710 let args = script_group.script.args().raw_data();
711 if args.len() < 22 {
712 return Err(UnlockError::Other(anyhow!(
713 "invalid script args length, expected not less than 22, got: {}",
714 args.len()
715 )));
716 }
717 let auth_content = if self.config.omni_lock_flags().contains(OmniLockFlags::ADMIN) {
719 self.config
720 .get_admin_config()
721 .unwrap()
722 .get_auth()
723 .auth_content()
724 } else {
725 self.config.id().auth_content()
726 };
727
728 let inputs = tx.inputs();
729 if tx.inputs().len() < 2 {
730 return Err(UnlockError::Other(anyhow!(
731 "expect more than 1 input, got: {}",
732 inputs.len()
733 )));
734 }
735 let mut matched = false;
736 for (_idx, input) in tx
737 .inputs()
738 .into_iter()
739 .enumerate()
740 .filter(|(idx, _input)| !script_group.input_indices.contains(idx))
741 {
742 if let Ok(output) = tx_dep_provider
743 .get_cell_async(&input.previous_output())
744 .await
745 {
746 let lock_hash = output.calc_lock_hash();
747 let h = &lock_hash.as_slice()[0..20];
748 if h == auth_content.as_bytes() {
749 matched = true;
750 break;
751 }
752 }
753 }
754 if !matched {
755 return Err(UnlockError::Other(anyhow!(
756 "can not find according owner lock input"
757 )));
758 }
759 Ok(matched)
760 }
761
762 async fn unlock_async(
763 &self,
764 tx: &TransactionView,
765 script_group: &ScriptGroup,
766 _tx_dep_provider: &dyn TransactionDependencyProvider,
767 ) -> Result<TransactionView, UnlockError> {
768 Ok(self.signer.sign_tx(tx, script_group)?)
769 }
770
771 async fn fill_placeholder_witness_async(
772 &self,
773 tx: &TransactionView,
774 script_group: &ScriptGroup,
775 _tx_dep_provider: &dyn TransactionDependencyProvider,
776 ) -> Result<TransactionView, UnlockError> {
777 let config = self.signer.config();
778 let lock_field = config.placeholder_witness_lock(self.signer.unlock_mode())?;
779 fill_witness_lock(tx, script_group, lock_field)
780 }
781}
782#[cfg(test)]
783mod anyhow_tests {
784 use anyhow::anyhow;
785 #[test]
786 fn test_unlock_error() {
787 let error = super::UnlockError::InvalidWitnessArgs(0);
788 let error = anyhow!(error);
789 assert_eq!("invalid witness args: witness index=`0`", error.to_string());
790 }
791}