ckb_sdk/unlock/
omni_lock.rs

1use core::hash;
2use std::fmt::Display;
3
4use crate::{
5    tx_builder::SinceSource,
6    types::{
7        omni_lock::{Auth, Identity as IdentityType, IdentityOpt, OmniLockWitnessLock},
8        xudt_rce_mol::SmtProofEntryVec,
9    },
10};
11use ckb_types::{
12    bytes::{BufMut, Bytes, BytesMut},
13    packed::WitnessArgs,
14    prelude::*,
15    H160, H256,
16};
17
18pub use ckb_types::prelude::Pack;
19use enum_repr_derive::{FromEnumToRepr, TryFromReprToEnum};
20use serde::{de::Unexpected, Deserialize, Serialize};
21use std::convert::TryFrom;
22
23use bitflags::bitflags;
24
25use super::{MultisigConfig, OmniUnlockMode};
26use thiserror::Error;
27
28#[derive(
29    Clone,
30    Copy,
31    Serialize,
32    Deserialize,
33    Debug,
34    Hash,
35    Eq,
36    PartialEq,
37    TryFromReprToEnum,
38    FromEnumToRepr,
39)]
40#[repr(u8)]
41#[derive(Default)]
42pub enum IdentityFlag {
43    /// The auth content represents the blake160 hash of a secp256k1 public key.
44    /// The lock script will perform secp256k1 signature verification, the same as the SECP256K1/blake160 lock.
45    #[default]
46    PubkeyHash = 0,
47    /// It follows the same unlocking methods used by Ethereum.
48    Ethereum = 1,
49    /// It follows the same unlocking methods used by EOS.
50    Eos = 2,
51    /// It follows the same unlocking methods used by Tron.
52    Tron = 3,
53    /// It follows the same unlocking methods used by Bitcoin
54    Bitcoin = 4,
55    ///  It follows the same unlocking methods used by Dogecoin.
56    Dogecoin = 5,
57    /// It follows the same unlocking method used by CKB MultiSig.
58    Multisig = 6,
59
60    /// The auth content that represents the blake160 hash of a lock script.
61    /// The lock script will check if the current transaction contains an input cell with a matching lock script.
62    /// Otherwise, it would return with an error. It's similar to P2SH in BTC.
63    OwnerLock = 0xFC,
64    /// The auth content that represents the blake160 hash of a preimage.
65    /// The preimage contains exec information that is used to delegate signature verification to another script via exec.
66    Exec = 0xFD,
67    /// The auth content that represents the blake160 hash of a preimage.
68    /// The preimage contains dynamic linking information that is used to delegate signature verification to the dynamic linking script.
69    /// The interface described in Swappable Signature Verification Protocol Spec is used here.
70    Dl = 0xFE,
71}
72
73#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Default)]
74pub struct Identity {
75    /// Indicate what's auth content of auth_content will be.
76    flag: IdentityFlag,
77    /// The auth content of the identity.
78    auth_content: H160,
79}
80
81impl Identity {
82    /// Create a new identity.
83    pub fn new(flag: IdentityFlag, auth_content: H160) -> Self {
84        Identity { flag, auth_content }
85    }
86
87    /// Create a pubkey hash algorithm Identity
88    /// # Arguments
89    /// * `pubkey_hash` blake160 hash of a public key.
90    pub fn new_pubkey_hash(pubkey_hash: H160) -> Self {
91        Self::new(IdentityFlag::PubkeyHash, pubkey_hash)
92    }
93
94    /// Create a mulltisig Identity
95    pub fn new_multisig(multisig_config: MultisigConfig) -> Self {
96        let blake160 = multisig_config.hash160();
97        Identity::new(IdentityFlag::Multisig, blake160)
98    }
99    /// Create an ethereum Identity omnilock with pubkey
100    /// # Arguments
101    /// * `pubkey_hash` keccak160 hash of public key
102    pub fn new_ethereum(pubkey_hash: H160) -> Self {
103        Self::new(IdentityFlag::Ethereum, pubkey_hash)
104    }
105
106    /// Create an ownerlock omnilock with according script hash.
107    /// # Arguments
108    /// * `script_hash` the proper blake160 hash of according ownerlock script.
109    pub fn new_ownerlock(script_hash: H160) -> Self {
110        Self::new(IdentityFlag::OwnerLock, script_hash)
111    }
112
113    /// convert the identify to smt_key.
114    pub fn to_smt_key(&self) -> [u8; 32] {
115        let mut ret = [0u8; 32];
116        ret[0] = self.flag as u8;
117        ret[1..21].copy_from_slice(self.auth_content.as_ref());
118        ret
119    }
120
121    /// get the flag
122    pub fn flag(&self) -> IdentityFlag {
123        self.flag
124    }
125    /// get the auth content of the identity
126    pub fn auth_content(&self) -> &H160 {
127        &self.auth_content
128    }
129
130    /// Parse the Identity from an u8 slice.
131    pub fn from_slice(slice: &[u8]) -> Result<Self, String> {
132        if slice.len() < 21 {
133            return Err("Not enough bytes to parse".to_string());
134        }
135        let flag = IdentityFlag::try_from(slice[0])
136            .map_err(|e| format!("can't parse {} to valide IdentityFlat.", e))?;
137        let auth_content = H160::from_slice(&slice[1..21]).map_err(|e| e.to_string())?;
138        Ok(Identity { flag, auth_content })
139    }
140}
141
142impl From<Identity> for [u8; 21] {
143    fn from(id: Identity) -> Self {
144        let mut res = [0u8; 21];
145        res[0] = id.flag as u8;
146        res[1..].copy_from_slice(id.auth_content.as_bytes());
147        res
148    }
149}
150
151impl From<Identity> for Vec<u8> {
152    fn from(id: Identity) -> Self {
153        let mut bytes: Vec<u8> = vec![id.flag as u8];
154        bytes.extend(id.auth_content.as_bytes());
155        bytes
156    }
157}
158
159impl From<Identity> for ckb_types::bytes::Bytes {
160    fn from(id: Identity) -> Self {
161        let v: Vec<u8> = id.into();
162        v.into()
163    }
164}
165
166impl Display for Identity {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        write!(f, "(")?;
169        let alternate = f.alternate();
170        if alternate {
171            write!(f, "0x")?;
172        }
173        write!(f, "{:02x},", self.flag as u8)?;
174        if alternate {
175            write!(f, "0x")?;
176        }
177        for x in self.auth_content.as_bytes() {
178            write!(f, "{:02x}", x)?;
179        }
180        write!(f, ")")?;
181        Ok(())
182    }
183}
184bitflags! {
185    #[derive(Serialize, Deserialize)]
186    pub struct OmniLockFlags: u8 {
187        /// administrator mode, flag is 1, affected args:  RC cell type ID, affected field:omni_identity/signature in OmniLockWitnessLock
188        const ADMIN = 1;
189        // anyone-can-pay mode, flag is 1<<1, affected args: minimum ckb/udt in ACP
190        const ACP = 1<<1;
191        /// time-lock mode, flag is 1<<2, affected args: since for timelock
192        const TIME_LOCK = 1<<2;
193        /// supply mode, flag is 1<<3, affected args: type script hash for supply
194        const SUPPLY = 1<<3;
195    }
196}
197
198impl Serialize for SmtProofEntryVec {
199    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
200    where
201        S: serde::Serializer,
202    {
203        serializer.serialize_newtype_struct("SmtProofEntryVec", &self.as_bytes())
204    }
205}
206
207impl<'de> Deserialize<'de> for SmtProofEntryVec {
208    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209    where
210        D: serde::Deserializer<'de>,
211    {
212        let bytes = Bytes::deserialize(deserializer)?;
213        SmtProofEntryVec::from_slice(bytes.as_ref()).map_err(|e| {
214            serde::de::Error::invalid_value(
215                Unexpected::Bytes(bytes.as_ref()),
216                &format!("can not convert the value to SmtProofEntryVec: {}", e).as_str(),
217            )
218        })
219    }
220}
221
222impl hash::Hash for SmtProofEntryVec {
223    fn hash<H>(&self, state: &mut H)
224    where
225        H: hash::Hasher,
226    {
227        self.as_slice().hash(state);
228    }
229}
230
231// impl Eq
232
233impl PartialEq for SmtProofEntryVec {
234    fn eq(&self, other: &SmtProofEntryVec) -> bool {
235        self.as_slice() == other.as_slice()
236    }
237}
238impl Eq for SmtProofEntryVec {}
239
240/// The info cell internal data of the supply mode.
241#[derive(Debug, Clone, Eq, PartialEq, Hash)]
242pub struct InfoCellData {
243    /// Current the version is 0, 1 byute
244    pub version: u8,
245    /// Only the current supply field can be updated during the transactions.16 bytes, little endian number
246    pub current_supply: u128,
247    /// The max supply limit.16 bytes, little endian number
248    pub max_supply: u128,
249    /// Type script hash. 32 bytes, sUDT type script hash
250    pub sudt_script_hash: H256,
251    /// Other data of variable length
252    pub other_data: Vec<u8>,
253}
254
255impl InfoCellData {
256    /// Create an InfoCellData with must exist fields.
257    /// # Arguments
258    /// * `current_supply` The current supply value
259    /// * `max_supply` The max supply value.
260    /// * `sudt_script_hash` The type script hash
261    pub fn new_simple(current_supply: u128, max_supply: u128, sudt_script_hash: H256) -> Self {
262        InfoCellData::new(current_supply, max_supply, sudt_script_hash, vec![])
263    }
264
265    /// Create an InfoCellData with all fields except `version` since it is 0 currently
266    pub fn new(
267        current_supply: u128,
268        max_supply: u128,
269        sudt_script_hash: H256,
270        other_data: Vec<u8>,
271    ) -> Self {
272        InfoCellData {
273            version: 0u8,
274            current_supply,
275            max_supply,
276            sudt_script_hash,
277            other_data,
278        }
279    }
280
281    /// Pack the data into bytes for the cell storage.
282    pub fn pack(&self) -> Bytes {
283        let len = 65 + self.other_data.len();
284        let mut bytes = BytesMut::with_capacity(len);
285        bytes.put_u8(self.version);
286        bytes.put_u128_le(self.current_supply);
287        bytes.put_u128_le(self.max_supply);
288        bytes.extend(self.sudt_script_hash.as_bytes());
289        bytes.extend(&self.other_data);
290        bytes.freeze()
291    }
292}
293
294/// The administrator mode configuration.
295#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Default)]
296pub struct AdminConfig {
297    /// The rc cell's type script hash, the type script should be a type id script.
298    rc_type_id: H256,
299    /// The smt proofs
300    proofs: SmtProofEntryVec,
301    /// The alternative auth content to the args part.
302    auth: Identity,
303    /// multisig cnfiguration
304    multisig_config: Option<MultisigConfig>,
305    /// If set the rce cell in the input
306    rce_in_input: bool,
307}
308
309impl AdminConfig {
310    pub fn set_rc_type_id(&mut self, rc_type_id: H256) {
311        self.rc_type_id = rc_type_id;
312    }
313    pub fn set_proofs(&mut self, proofs: SmtProofEntryVec) {
314        self.proofs = proofs;
315    }
316    pub fn rc_type_id(&self) -> &H256 {
317        &self.rc_type_id
318    }
319    pub fn proofs(&self) -> &SmtProofEntryVec {
320        &self.proofs
321    }
322
323    /// set the additional auth, it will be used to sign the transaction.
324    pub fn set_auth(&mut self, auth: Identity) {
325        self.auth = auth;
326    }
327
328    /// return a reference to the auth content
329    pub fn get_auth(&self) -> &Identity {
330        &self.auth
331    }
332
333    /// return the content of the multisig config
334    pub fn get_multisig_config(&self) -> Option<&MultisigConfig> {
335        self.multisig_config.as_ref()
336    }
337
338    /// Set the config rce_in_input to the specified value
339    pub fn set_rce_in_input(&mut self, value: bool) {
340        self.rce_in_input = value;
341    }
342
343    /// Get the configuration about if smt is in the input list.
344    pub fn rce_in_input(&self) -> bool {
345        self.rce_in_input
346    }
347
348    pub fn new(
349        rc_type_id: H256,
350        proofs: SmtProofEntryVec,
351        auth: Identity,
352        multisig_config: Option<MultisigConfig>,
353        rce_in_input: bool,
354    ) -> AdminConfig {
355        AdminConfig {
356            rc_type_id,
357            proofs,
358            auth,
359            multisig_config,
360            rce_in_input,
361        }
362    }
363}
364
365#[derive(Error, Debug)]
366pub enum ConfigError {
367    #[error("there is no admin configuration in the OmniLockConfig")]
368    NoAdminConfig,
369
370    #[error("there is no multisig config in the OmniLockConfig")]
371    NoMultiSigConfig,
372
373    #[error(transparent)]
374    Other(#[from] anyhow::Error),
375}
376
377#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Default)]
378pub struct OmniLockAcpConfig {
379    /// Tthe minimal transfer amount will be 10^ckb_minimum, if ckb_minimum is 0, means no minimum is enforced on the transfer operation.
380    pub ckb_minimum: u8,
381    /// Tthe minimal transfer amount will be 10^udt_minimum, if udt_minimum is 0, means no minimum is enforced on the transfer operation.
382    pub udt_minimum: u8,
383}
384
385impl OmniLockAcpConfig {
386    pub fn new(ckb_minimum: u8, udt_minimum: u8) -> Self {
387        OmniLockAcpConfig {
388            ckb_minimum,
389            udt_minimum,
390        }
391    }
392}
393
394/// OmniLock configuration
395/// The lock argument has the following data structure:
396/// 1. 21 byte auth
397/// 2. 1 byte Omnilock flags
398/// 3. 32 byte RC cell type ID, optional
399/// 4. 2 bytes minimum ckb/udt in ACP, optional
400/// 5. 8 bytes since for time lock, optional
401/// 6. 32 bytes type script hash for supply, optional
402#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq)]
403pub struct OmniLockConfig {
404    /// The auth id of the OmniLock
405    id: Identity,
406    /// The multisig config.
407    multisig_config: Option<MultisigConfig>,
408    /// The omni lock flags, it indicates whether the other four fields exist.
409    omni_lock_flags: OmniLockFlags,
410    /// The administrator configuration.
411    admin_config: Option<AdminConfig>,
412    /// The acp configuration
413    acp_config: Option<OmniLockAcpConfig>,
414    /// 8 bytes since for time lock
415    time_lock_config: Option<u64>,
416    // 32 bytes type script hash
417    info_cell: Option<H256>,
418}
419
420impl OmniLockConfig {
421    /// Create a pubkey hash algorithm omnilock with proper argument
422    /// # Arguments
423    /// * `lock_arg` blake160 hash of a public key.
424    pub fn new_pubkey_hash(lock_arg: H160) -> Self {
425        Self::new(IdentityFlag::PubkeyHash, lock_arg)
426    }
427
428    pub fn new_multisig(multisig_config: MultisigConfig) -> Self {
429        let blake160 = multisig_config.hash160();
430        OmniLockConfig {
431            id: Identity {
432                flag: IdentityFlag::Multisig,
433                auth_content: blake160,
434            },
435            multisig_config: Some(multisig_config),
436            omni_lock_flags: OmniLockFlags::empty(),
437            admin_config: None,
438            acp_config: None,
439            time_lock_config: None,
440            info_cell: None,
441        }
442    }
443    /// Create an ethereum algorithm omnilock with pubkey
444    ///
445    /// # Arguments
446    ///
447    /// * `pubkey_hash` - a ehtereum address of an account.
448    ///
449    /// ```
450    /// // pubkey is a public ethereum address
451    /// use ckb_sdk::unlock::OmniLockConfig;
452    /// use ckb_sdk::util::keccak160;
453    /// use ckb_crypto::secp::Pubkey;
454    ///
455    /// let pubkey = Pubkey::from([0u8; 64]);
456    /// let pubkey_hash = keccak160(pubkey.as_ref());
457    /// let config = OmniLockConfig::new_ethereum(pubkey_hash);
458    /// ```
459    pub fn new_ethereum(pubkey_hash: H160) -> Self {
460        Self::new(IdentityFlag::Ethereum, pubkey_hash)
461    }
462
463    /// Create an ownerlock omnilock with according script hash.
464    /// # Arguments
465    /// * `script_hash` the proper blake160 hash of according ownerlock script.
466    pub fn new_ownerlock(script_hash: H160) -> Self {
467        Self::new(IdentityFlag::OwnerLock, script_hash)
468    }
469
470    /// Create a new OmniLockConfig
471    pub fn new(flag: IdentityFlag, auth_content: H160) -> Self {
472        let auth_content = match flag {
473            IdentityFlag::PubkeyHash | IdentityFlag::Ethereum | IdentityFlag::OwnerLock => {
474                auth_content
475            }
476            _ => H160::from_slice(&[0; 20]).unwrap(),
477        };
478
479        OmniLockConfig {
480            id: Identity { flag, auth_content },
481            multisig_config: None,
482            omni_lock_flags: OmniLockFlags::empty(),
483            admin_config: None,
484            acp_config: None,
485            time_lock_config: None,
486            info_cell: None,
487        }
488    }
489
490    /// Set the admin cofiguration, and set the OmniLockFlags::ADMIN flag.
491    /// # Arguments
492    /// * `admin_config` The new admin config.
493    pub fn set_admin_config(&mut self, admin_config: AdminConfig) {
494        self.omni_lock_flags.set(OmniLockFlags::ADMIN, true);
495        self.admin_config = Some(admin_config);
496    }
497
498    /// Remove the admin configuration, set it to `None`, and clear the OmniLockFlags::ADMIN flag.
499    pub fn clear_admin_config(&mut self) {
500        self.omni_lock_flags.set(OmniLockFlags::ADMIN, false);
501        self.admin_config = None;
502    }
503
504    /// Set the acp configuration, and set the OmniLockFlags::ACP flag.
505    pub fn set_acp_config(&mut self, acp_config: OmniLockAcpConfig) {
506        self.omni_lock_flags.set(OmniLockFlags::ACP, true);
507        self.acp_config = Some(acp_config);
508    }
509
510    /// Remove the acp config, set it to None, and clear the OmniLockFlags::ACP flag.
511    pub fn clear_acp_config(&mut self) {
512        self.omni_lock_flags.set(OmniLockFlags::ACP, false);
513        self.acp_config = None;
514    }
515    /// Set the time lock config with raw since value, and set the OmniLockFlags::TIME_LOCK flag.
516    pub fn set_time_lock_config(&mut self, since: u64) {
517        self.omni_lock_flags.set(OmniLockFlags::TIME_LOCK, true);
518        self.time_lock_config = Some(since);
519    }
520    /// Remove the time lock config, set it to None, and clear the OmniLockFlags::TIME_LOCK flag.
521    pub fn clear_time_lock_config(&mut self) {
522        self.omni_lock_flags.set(OmniLockFlags::TIME_LOCK, false);
523        self.time_lock_config = None;
524    }
525
526    /// Set the info cell to the configuration, and set the OmniLockFlags::SUPPLY to omni_lock_flags.
527    pub fn set_info_cell(&mut self, type_script_hash: H256) {
528        self.omni_lock_flags.set(OmniLockFlags::SUPPLY, true);
529        self.info_cell = Some(type_script_hash);
530    }
531
532    /// Clear the info cell to None, and clear OmniLockFlags::SUPPLY from omni_lock_flags.
533    pub fn clear_info_cell(&mut self) {
534        self.omni_lock_flags.set(OmniLockFlags::SUPPLY, false);
535        self.info_cell = None;
536    }
537
538    pub fn id(&self) -> &Identity {
539        &self.id
540    }
541
542    /// Return the reference content of the multisig config.
543    /// If the multisig config is None, it will panic.
544    pub fn multisig_config(&self) -> Option<&MultisigConfig> {
545        self.multisig_config.as_ref()
546    }
547
548    pub fn omni_lock_flags(&self) -> &OmniLockFlags {
549        &self.omni_lock_flags
550    }
551
552    pub fn use_rc(&self) -> bool {
553        self.admin_config.is_some()
554    }
555
556    /// Build lock script arguments
557    pub fn build_args(&self) -> Bytes {
558        let mut bytes = BytesMut::with_capacity(128);
559
560        // auth
561        bytes.put_u8(self.id.flag as u8);
562        bytes.put(self.id.auth_content.as_ref());
563        bytes.put_u8(self.omni_lock_flags.bits);
564
565        if let Some(config) = self.admin_config.as_ref() {
566            bytes.put(config.rc_type_id.as_bytes());
567        }
568        if let Some(config) = self.acp_config.as_ref() {
569            bytes.put_u8(config.ckb_minimum);
570            bytes.put_u8(config.udt_minimum);
571        }
572        if let Some(since) = self.time_lock_config.as_ref() {
573            bytes.extend(since.to_le_bytes().iter());
574        }
575
576        if let Some(info_cell) = self.info_cell.as_ref() {
577            bytes.extend(info_cell.as_bytes());
578        }
579        bytes.freeze()
580    }
581
582    /// return the internal reference of admin_config
583    pub fn get_admin_config(&self) -> Option<&AdminConfig> {
584        self.admin_config.as_ref()
585    }
586
587    pub fn get_info_cell(&self) -> Option<&H256> {
588        self.info_cell.as_ref()
589    }
590
591    /// Calculate script args length
592    pub fn get_args_len(&self) -> usize {
593        let mut len = 22;
594        if self.omni_lock_flags.contains(OmniLockFlags::ADMIN) {
595            len += 32;
596        }
597        if self.omni_lock_flags.contains(OmniLockFlags::ACP) {
598            len += 2;
599        }
600        if self.omni_lock_flags.contains(OmniLockFlags::TIME_LOCK) {
601            len += 8;
602        }
603        if self.omni_lock_flags.contains(OmniLockFlags::SUPPLY) {
604            len += 32;
605        }
606        len
607    }
608
609    /// Get the since source from args.
610    pub fn get_since_source(&self) -> SinceSource {
611        if self.omni_lock_flags.contains(OmniLockFlags::TIME_LOCK) {
612            let mut offset = 22;
613            if self.omni_lock_flags.contains(OmniLockFlags::ADMIN) {
614                offset += 32;
615            }
616            if self.omni_lock_flags.contains(OmniLockFlags::ACP) {
617                offset += 2;
618            }
619            SinceSource::LockArgs(offset)
620        } else {
621            SinceSource::Value(0)
622        }
623    }
624
625    /// Indicate whether is a sighash type.
626    pub fn is_pubkey_hash(&self) -> bool {
627        self.id.flag == IdentityFlag::PubkeyHash
628    }
629
630    /// Indicate whether is a ethereum type.
631    pub fn is_ethereum(&self) -> bool {
632        self.id.flag == IdentityFlag::Ethereum
633    }
634
635    /// Check if it is a mutlisig flag.
636    pub fn is_multisig(&self) -> bool {
637        self.id.flag == IdentityFlag::Multisig
638    }
639
640    /// Check if it is a ownerlock flag.
641    pub fn is_ownerlock(&self) -> bool {
642        self.id.flag == IdentityFlag::OwnerLock
643    }
644
645    pub fn placeholder_witness_lock(
646        &self,
647        unlock_mode: OmniUnlockMode,
648    ) -> Result<Bytes, ConfigError> {
649        let mut builder = match self.id.flag {
650            IdentityFlag::PubkeyHash | IdentityFlag::Ethereum => OmniLockWitnessLock::new_builder()
651                .signature(Some(Bytes::from(vec![0u8; 65])).pack()),
652            IdentityFlag::Multisig => {
653                let multisig_config = match unlock_mode {
654                    OmniUnlockMode::Admin => self
655                        .admin_config
656                        .as_ref()
657                        .ok_or(ConfigError::NoAdminConfig)?
658                        .multisig_config
659                        .as_ref()
660                        .ok_or(ConfigError::NoMultiSigConfig)?,
661                    OmniUnlockMode::Normal => self
662                        .multisig_config
663                        .as_ref()
664                        .ok_or(ConfigError::NoMultiSigConfig)?,
665                };
666                let config_data = multisig_config.to_witness_data();
667                let multisig_len = config_data.len() + multisig_config.threshold() as usize * 65;
668                let mut omni_sig = vec![0u8; multisig_len];
669                omni_sig[..config_data.len()].copy_from_slice(&config_data);
670                OmniLockWitnessLock::new_builder().signature(Some(Bytes::from(omni_sig)).pack())
671            }
672            IdentityFlag::OwnerLock => OmniLockWitnessLock::new_builder(),
673            _ => todo!("to support other placeholder_witness_lock implementions"),
674        };
675
676        if unlock_mode == OmniUnlockMode::Admin {
677            if let Some(config) = self.admin_config.as_ref() {
678                let mut temp = [0u8; 21];
679                temp[0] = config.auth.flag as u8;
680                temp[1..21].copy_from_slice(config.auth.auth_content.as_bytes());
681                let auth = Auth::from_slice(&temp).unwrap();
682                let ident = IdentityType::new_builder()
683                    .identity(auth)
684                    .proofs(config.proofs.clone())
685                    .build();
686
687                let ident_opt = IdentityOpt::new_builder().set(Some(ident)).build();
688                builder = builder.omni_identity(ident_opt);
689            } else {
690                return Err(ConfigError::NoAdminConfig);
691            }
692        }
693        Ok(builder.build().as_bytes())
694    }
695
696    /// Build zero lock content for signature
697    pub fn zero_lock(&self, unlock_mode: OmniUnlockMode) -> Result<Bytes, ConfigError> {
698        let len = self.placeholder_witness_lock(unlock_mode)?.len();
699        Ok(Bytes::from(vec![0u8; len]))
700    }
701
702    /// Create a zero lock witness placeholder
703    pub fn placeholder_witness(
704        &self,
705        unlock_mode: OmniUnlockMode,
706    ) -> Result<WitnessArgs, ConfigError> {
707        match self.id.flag {
708            IdentityFlag::PubkeyHash | IdentityFlag::Ethereum | IdentityFlag::Multisig => {
709                let lock = self.placeholder_witness_lock(unlock_mode)?;
710                Ok(WitnessArgs::new_builder().lock(Some(lock).pack()).build())
711            }
712            IdentityFlag::OwnerLock => {
713                if self.admin_config.is_some() {
714                    let lock = self.placeholder_witness_lock(unlock_mode)?;
715                    Ok(WitnessArgs::new_builder().lock(Some(lock).pack()).build())
716                } else {
717                    Ok(WitnessArgs::default())
718                }
719            }
720            _ => todo!("to support other placeholder_witness implementions"),
721        }
722    }
723}
724
725#[cfg(test)]
726mod tests {
727    use ckb_types::packed::Byte;
728
729    use crate::{
730        types::xudt_rce_mol::{SmtProof, SmtProofEntry, SmtProofEntryVec},
731        unlock::omni_lock::AdminConfig,
732    };
733    use ckb_types::{h256, prelude::*};
734    #[test]
735    fn test_adminconfig_serde() {
736        let mut i = (0u8..=255u8).cycle();
737        let type_id = h256!("0x1234567890abcdeffedcba0987654321");
738        let mut proofs_builder = SmtProofEntryVec::new_builder();
739        for _ in 0..2 {
740            let proof = SmtProof::new_builder()
741                .extend(i.by_ref().take(8).map(Byte::new))
742                .build();
743            let entry = SmtProofEntry::new_builder()
744                .mask(Byte::new(0))
745                .proof(proof)
746                .build();
747            proofs_builder = proofs_builder.push(entry);
748        }
749        let cfg = AdminConfig {
750            rc_type_id: type_id,
751            proofs: proofs_builder.build(),
752            ..Default::default()
753        };
754        let x = serde_json::to_string_pretty(&cfg).unwrap();
755        let cfg2: AdminConfig = serde_json::from_str(&x).unwrap();
756        assert_eq!(cfg, cfg2);
757    }
758}
759#[cfg(test)]
760mod anyhow_tests {
761    use anyhow::anyhow;
762    #[test]
763    fn test_config_error() {
764        let error = super::ConfigError::NoAdminConfig;
765        let error = anyhow!(error);
766        assert_eq!(
767            "there is no admin configuration in the OmniLockConfig",
768            error.to_string()
769        );
770    }
771}