1#![no_std]
2
3extern crate alloc;
4
5use alloc::vec::Vec;
6use ckb_std::{
7 ckb_constants::Source,
8 debug,
9 error::SysError,
10 high_level::{load_cell_type_hash, load_input, load_script, load_script_hash},
11 syscalls::load_cell,
12};
13use molecule::prelude::Entity;
14
15pub enum Error {
16 Syscall(SysError),
17 Native(TypeIDError),
18}
19pub enum TypeIDError {
20 InvalidTypeIDCellNum = 4,
22 TypeIDNotMatch,
24 ArgsLengthNotEnough,
26}
27
28impl From<SysError> for Error {
29 fn from(s: SysError) -> Self {
30 Error::Syscall(s)
31 }
32}
33
34fn has_type_id_cell(index: usize, source: Source) -> bool {
35 let mut buf = Vec::new();
36 match load_cell(&mut buf, 0, index, source) {
37 Ok(_) => true,
38 Err(e) => {
39 if let SysError::LengthNotEnough(_) = e {
41 return true;
42 }
43 debug!("load cell err: {:?}", e);
44 false
45 }
46 }
47}
48
49fn locate_first_type_id_output_index() -> Result<usize, Error> {
50 let current_script_hash = load_script_hash()?;
51
52 let mut i = 0;
53 loop {
54 let type_hash = load_cell_type_hash(i, Source::Output)?;
55
56 if type_hash == Some(current_script_hash) {
57 break;
58 }
59 i += 1
60 }
61 Ok(i)
62}
63
64pub fn validate_type_id(type_id: [u8; 32]) -> Result<(), Error> {
67 if has_type_id_cell(1, Source::GroupInput) || has_type_id_cell(1, Source::GroupOutput) {
68 debug!("There can only be at most one input and at most one output type ID cell!");
69 return Err(Error::Native(TypeIDError::InvalidTypeIDCellNum));
70 }
71
72 if !has_type_id_cell(0, Source::GroupInput) {
73 let index = locate_first_type_id_output_index()?;
75
76 let input = load_input(0, Source::Input)?;
80 let mut blake2b = blake2b_rs::Blake2bBuilder::new(32)
81 .personal(b"ckb-default-hash")
82 .build();
83 blake2b.update(input.as_slice());
84 blake2b.update(&index.to_le_bytes());
85 let mut ret = [0; 32];
86 blake2b.finalize(&mut ret);
87
88 if ret != type_id {
89 debug!("Invalid type ID!");
90 return Err(Error::Native(TypeIDError::TypeIDNotMatch));
91 }
92 }
93 Ok(())
94}
95
96pub fn load_type_id_from_script_args(offset: usize) -> Result<[u8; 32], Error> {
99 let script = load_script()?;
100 let args = script.as_reader().args();
101 if offset + 32 > args.raw_data().len() {
102 debug!("Length of type id is incorrect!");
103 return Err(Error::Native(TypeIDError::ArgsLengthNotEnough));
104 }
105 let mut ret = [0; 32];
106 ret.copy_from_slice(&args.raw_data()[offset..offset + 32]);
107 Ok(ret)
108}