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