1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
use lazy_static::lazy_static;

use sparse_merkle_tree::{default_store::DefaultStore, SparseMerkleTree, H256 as SmtH256};

use crate::types::xudt_rce_mol::{
    RCDataBuilder, RCDataUnion, RCRuleBuilder, SmtProofBuilder, SmtProofEntryBuilder,
    SmtProofEntryVec, SmtProofEntryVecBuilder,
};
use bytes::Bytes;
use ckb_hash::{new_blake2b, Blake2b};
use ckb_types::{molecule, prelude::*};
use sparse_merkle_tree::traits::Hasher;
use thiserror::Error;

lazy_static! {
    pub static ref SMT_EXISTING: SmtH256 = SmtH256::from([
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0,
    ]);
    pub static ref SMT_NOT_EXISTING: SmtH256 = SmtH256::from([
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0,
    ]);
}

#[allow(clippy::upper_case_acronyms)]
type SMT = SparseMerkleTree<CKBBlake2bHasher, SmtH256, DefaultStore<SmtH256>>;
pub type Result<T> = ::core::result::Result<T, RcDataError>;

#[derive(Debug, Clone, PartialEq, Error)]
pub enum RcDataError {
    #[error("fail to build the smt tree:`{0}`")]
    BuildTree(String),
    #[error("fail to compile proof, reason:`{0}`")]
    CompileProof(String),
}

// on(1): white list
// off(0): black list
const WHITE_BLACK_LIST_MASK: u8 = 0x2;

// on(1): emergency halt mode
// off(0): not int emergency halt mode
const EMERGENCY_HALT_MODE_MASK: u8 = 0x1;
pub struct CKBBlake2bHasher(Blake2b);

impl Default for CKBBlake2bHasher {
    fn default() -> Self {
        let blake2b = new_blake2b();
        CKBBlake2bHasher(blake2b)
    }
}

impl Hasher for CKBBlake2bHasher {
    fn write_h256(&mut self, h: &SmtH256) {
        self.0.update(h.as_slice());
    }
    fn finish(self) -> SmtH256 {
        let mut hash = [0u8; 32];
        self.0.finalize(&mut hash);
        hash.into()
    }
    fn write_byte(&mut self, b: u8) {
        self.0.update(&[b][..]);
    }
}

/// The list type of an omnilock admin rule list type.
pub enum ListType {
    /// Indicate it's a white list.
    White,
    /// Indicate it's a black list.
    Black,
}

/// a builder to build rc_rule
pub struct RcRuleDataBuilder {
    /// the smt tree
    smt: SMT,
    /// the list type
    list_type: ListType,
    /// indicate if the rule is emergency
    is_emergency: bool,
}

impl RcRuleDataBuilder {
    pub fn new(list_type: ListType, is_emergency: bool) -> Self {
        let smt = SMT::default();
        RcRuleDataBuilder {
            smt,
            list_type,
            is_emergency,
        }
    }
    /// create a default smt tree with initial smt values.
    pub fn new_smt(pairs: &[(SmtH256, SmtH256)], list_type: ListType, is_emergency: bool) -> Self {
        let mut builder = RcRuleDataBuilder::new(list_type, is_emergency);
        builder.update(pairs);
        builder
    }

    /// update key/value pair into the smt tree
    pub fn update(&mut self, pairs: &[(SmtH256, SmtH256)]) {
        for (key, value) in pairs {
            self.smt.update(*key, *value).unwrap();
        }
    }
    /// calculate the root hash of the smt tree.
    pub fn root(&self) -> SmtH256 {
        *self.smt.root()
    }

    /// Build smt with the given hashes
    /// # Arguments
    /// * `hashes` The given the hashes.
    pub fn update_hashes(&mut self, hashes: &[SmtH256]) {
        let pairs: Vec<(SmtH256, SmtH256)> =
            hashes.iter().map(|hash| (*hash, *SMT_EXISTING)).collect();
        self.update(&pairs);
    }

    /// Build a smt tree with it's keys and gnerate proofs with the according keys.
    /// # Arguments
    /// * `keys` - The keys to generate the proofs.
    /// # Return
    /// The smt_tree root and the proofs of the proof_keys.
    pub fn proof_keys(&mut self, keys: &[SmtH256]) -> Result<Vec<u8>> {
        let proof = self
            .smt
            .merkle_proof(keys.to_vec())
            .map_err(|err| RcDataError::BuildTree(err.to_string()))?;
        let compiled_proof = proof
            .compile(keys.to_vec())
            .map_err(|e| RcDataError::CompileProof(e.to_string()))?;
        Ok(compiled_proof.into())
    }

    /// Build the rc_rule after key/value pairs are set.
    pub fn build_rc_rule(&self) -> Bytes {
        let smt_root = self.smt.root();
        let mut flags: u8 = 0;

        if let ListType::White = self.list_type {
            flags ^= WHITE_BLACK_LIST_MASK;
        }
        if self.is_emergency {
            flags ^= EMERGENCY_HALT_MODE_MASK;
        }
        let rcrule = RCRuleBuilder::default()
            .flags(flags.into())
            .smt_root(Into::<[u8; 32]>::into(*smt_root).pack())
            .build();
        let res = RCDataBuilder::default()
            .set(RCDataUnion::RCRule(rcrule))
            .build();
        res.as_bytes()
    }

    /// Build a proof and a rc_rule
    /// # Arguments
    /// * `on` - If the given smt_keys are on the smt tree.
    /// * `smt_key` - A list of smt keys.
    ///
    /// # Return
    /// A proof and the according rc rule.
    pub fn build_single_proof(
        &mut self,
        smt_key: &[SmtH256],
        on: bool,
    ) -> Result<(Vec<u8>, Bytes)> {
        let hash = if on { smt_key } else { &[] };
        self.update_hashes(hash);
        let proof = self.proof_keys(smt_key)?;

        let rc_rule = self.build_rc_rule();
        Ok((proof, rc_rule))
    }
}

/// Indicate which the rule is applied to.
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Mask {
    /// apply none to input or output.
    Neither = 0,
    /// apply to input
    Input = 1,
    /// apply to output.
    Output = 2,
    /// apply to both input and output.
    Both = 3,
}
pub struct ProofWithMask {
    pub proof: Vec<u8>,
    pub mask: Mask,
}

impl ProofWithMask {
    pub fn new(proof: Vec<u8>, mask: Mask) -> Self {
        ProofWithMask { proof, mask }
    }
}
#[derive(Default)]
pub struct RcRuleVecBuilder {
    proofs: Vec<ProofWithMask>,
    rc_rules: Vec<Bytes>,
}

impl RcRuleVecBuilder {
    pub fn new() -> RcRuleVecBuilder {
        RcRuleVecBuilder::default()
    }

    /// Add proof and according rule to the list.
    pub fn add_rule(&mut self, proof: ProofWithMask, rc_rule: Bytes) {
        self.proofs.push(proof);
        self.rc_rules.push(rc_rule);
    }

    /// build a rule and single proof and add them with self.add_rule.
    /// # Arguments
    /// * `smt_key` The keys.
    /// * `mask`  The mask indicate which rule to apply.
    /// * `list_type` The black/white list type.
    /// * `is_emergency` if it's emergency
    pub fn build_single_proof_and_rule(
        &mut self,
        smt_key: &[SmtH256],
        mask: Mask,
        list_type: ListType,
        is_emergency: bool,
        on: bool,
    ) -> Result<()> {
        let mut rc_rule_builder = RcRuleDataBuilder::new(list_type, is_emergency);
        let (proof, rc_rule) = rc_rule_builder.build_single_proof(smt_key, on)?;
        let proof_with_mask = ProofWithMask::new(proof, mask);
        self.add_rule(proof_with_mask, rc_rule);

        Ok(())
    }

    pub fn build_proofs(&self) -> SmtProofEntryVec {
        let mut builder = SmtProofEntryVecBuilder::default();
        for ProofWithMask { proof, mask } in &self.proofs {
            let proof_builder = SmtProofBuilder::default().set(
                proof
                    .iter()
                    .map(|v| molecule::prelude::Byte::new(*v))
                    .collect(),
            );

            let temp = SmtProofEntryBuilder::default()
                .proof(proof_builder.build())
                .mask((*mask as u8).into());
            builder = builder.push(temp.build());
        }
        builder.build()
    }

    pub fn proofs(&self) -> &Vec<ProofWithMask> {
        &self.proofs
    }

    pub fn rc_rules(&self) -> &Vec<Bytes> {
        &self.rc_rules
    }
}

#[cfg(test)]
mod tests {
    use ckb_types::prelude::*;
    use sparse_merkle_tree::{CompiledMerkleProof, H256 as SmtH256};

    use crate::types::xudt_rce_mol::{RCData, RCDataUnion};

    use super::*;
    #[test]
    fn test_build_smt_on_bl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::Black, false);
        builder.update_hashes(&[smt_key]);
        let (root, proof) = (builder.root(), builder.proof_keys(&[smt_key]).unwrap());
        let compiled_proof = CompiledMerkleProof(proof);
        assert!(!compiled_proof
            .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_NOT_EXISTING)])
            .unwrap());

        let mut builder = RcRuleDataBuilder::new(ListType::Black, false);
        let (root, proof) = (builder.root(), builder.proof_keys(&[smt_key]).unwrap());
        let compiled_proof = CompiledMerkleProof(proof);
        assert!(compiled_proof
            .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_NOT_EXISTING)])
            .unwrap());
    }

    #[test]
    fn test_build_smt_on_wl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::White, false);
        builder.update_hashes(&[smt_key]);
        let (root, proof) = (builder.root(), builder.proof_keys(&[smt_key]).unwrap());
        let compiled_proof = CompiledMerkleProof(proof);
        assert!(compiled_proof
            .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
            .unwrap());

        let mut builder = RcRuleDataBuilder::new(ListType::White, false);
        let (root, proof) = (builder.root(), builder.proof_keys(&[smt_key]).unwrap());
        let compiled_proof = CompiledMerkleProof(proof);
        assert!(!compiled_proof
            .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
            .unwrap());
    }

    #[test]
    fn test_generate_single_proof_on_wl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::White, false);
        builder.update_hashes(&[smt_key]);
        let (proof, rc_rule) = builder.build_single_proof(&[smt_key], true).unwrap();
        let compiled_proof = CompiledMerkleProof(proof);
        let rc_data = RCData::from_slice(&rc_rule).unwrap();
        let rcdata_union = rc_data.to_enum();
        if let RCDataUnion::RCRule(rc_rule) = rcdata_union {
            let root = rc_rule.smt_root();
            let mut root_hash = [0u8; 32];
            root_hash.copy_from_slice(root.as_slice());
            let root = SmtH256::from(root_hash);
            assert!(compiled_proof
                .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
                .unwrap());

            let flags: u8 = rc_rule.flags().into();
            assert_eq!(flags, WHITE_BLACK_LIST_MASK);
        } else {
            panic!("expected rc_rule");
        }
    }
    #[test]
    fn test_generate_single_proof_off_wl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::White, false);
        let (proof, rc_rule) = builder.build_single_proof(&[smt_key], false).unwrap();
        let compiled_proof = CompiledMerkleProof(proof);
        let rc_data = RCData::from_slice(&rc_rule).unwrap();
        let rcdata_union = rc_data.to_enum();
        if let RCDataUnion::RCRule(rc_rule) = rcdata_union {
            let root = rc_rule.smt_root();
            let mut root_hash = [0u8; 32];
            root_hash.copy_from_slice(root.as_slice());
            let root = SmtH256::from(root_hash);
            assert!(!compiled_proof
                .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
                .unwrap());

            let flags: u8 = rc_rule.flags().into();
            assert_eq!(flags, WHITE_BLACK_LIST_MASK);
        } else {
            panic!("expected rc_rule");
        }
    }
    #[test]
    fn test_generate_single_proof_on_bl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::Black, false);
        let (proof, rc_rule) = builder.build_single_proof(&[smt_key], true).unwrap();
        let compiled_proof = CompiledMerkleProof(proof);
        let rc_data = RCData::from_slice(&rc_rule).unwrap();
        let rcdata_union = rc_data.to_enum();
        if let RCDataUnion::RCRule(rc_rule) = rcdata_union {
            let root = rc_rule.smt_root();
            let mut root_hash = [0u8; 32];
            root_hash.copy_from_slice(root.as_slice());
            let root = SmtH256::from(root_hash);
            assert!(compiled_proof
                .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
                .unwrap());

            let flags: u8 = rc_rule.flags().into();
            assert_eq!(flags, 0);
        } else {
            panic!("expected rc_rule");
        }
    }
    #[test]
    fn test_generate_single_proof_off_bl() {
        let smt_key = SmtH256::zero();
        let mut builder = RcRuleDataBuilder::new(ListType::Black, false);
        let (proof, rc_rule) = builder.build_single_proof(&[smt_key], false).unwrap();
        let compiled_proof = CompiledMerkleProof(proof);
        let rc_data = RCData::from_slice(&rc_rule).unwrap();
        let rcdata_union = rc_data.to_enum();
        if let RCDataUnion::RCRule(rc_rule) = rcdata_union {
            let root = rc_rule.smt_root();
            let mut root_hash = [0u8; 32];
            root_hash.copy_from_slice(root.as_slice());
            let root = SmtH256::from(root_hash);
            assert!(!compiled_proof
                .verify::<CKBBlake2bHasher>(&root, vec![(smt_key, *SMT_EXISTING)])
                .unwrap());

            let flags: u8 = rc_rule.flags().into();
            assert_eq!(flags, 0);
        } else {
            panic!("expected rc_rule");
        }
    }
}

#[cfg(test)]
mod anyhow_tests {
    // test cases make sure new added exception won't breadk `anyhow!(e_variable)` usage,
    use anyhow::anyhow;
    #[test]
    fn test_rc_data_error() {
        let error = super::RcDataError::BuildTree("BuildTree".to_string());
        let error = anyhow!(error);
        assert_eq!("fail to build the smt tree:`BuildTree`", error.to_string());
    }
}