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
use ckb_occupied_capacity::Result as CapacityResult;

use crate::{core::Capacity, packed, prelude::*};

impl packed::Script {
    pub fn occupied_capacity(&self) -> CapacityResult<Capacity> {
        Capacity::bytes(self.args().raw_data().len() + 32 + 1)
    }
}

impl packed::CellOutput {
    pub fn occupied_capacity(&self, data_capacity: Capacity) -> CapacityResult<Capacity> {
        Capacity::bytes(8)
            .and_then(|x| x.safe_add(data_capacity))
            .and_then(|x| self.lock().occupied_capacity().and_then(|y| y.safe_add(x)))
            .and_then(|x| {
                self.type_()
                    .to_opt()
                    .as_ref()
                    .map(packed::Script::occupied_capacity)
                    .transpose()
                    .and_then(|y| y.unwrap_or_else(Capacity::zero).safe_add(x))
            })
    }

    pub fn is_lack_of_capacity(&self, data_capacity: Capacity) -> CapacityResult<bool> {
        self.occupied_capacity(data_capacity)
            .map(|cap| cap > self.capacity().unpack())
    }
}

impl packed::CellOutputBuilder {
    pub fn build_exact_capacity(
        self,
        data_capacity: Capacity,
    ) -> CapacityResult<packed::CellOutput> {
        Capacity::bytes(8)
            .and_then(|x| x.safe_add(data_capacity))
            .and_then(|x| self.lock.occupied_capacity().and_then(|y| y.safe_add(x)))
            .and_then(|x| {
                self.type_
                    .to_opt()
                    .as_ref()
                    .map(packed::Script::occupied_capacity)
                    .transpose()
                    .and_then(|y| y.unwrap_or_else(Capacity::zero).safe_add(x))
            })
            .map(|x| self.capacity(x.pack()).build())
    }
}

impl packed::CellOutputVec {
    pub fn total_capacity(&self) -> CapacityResult<Capacity> {
        self.as_reader()
            .iter()
            .map(|output| {
                let cap: Capacity = output.capacity().unpack();
                cap
            })
            .try_fold(Capacity::zero(), Capacity::safe_add)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        core::{capacity_bytes, Capacity},
        packed,
        prelude::*,
    };

    #[test]
    fn script_occupied_capacity() {
        let testcases = vec![
            (vec![], 32 + 1),
            (vec![0], 1 + 32 + 1),
            (vec![1], 1 + 32 + 1),
            (vec![0, 0], 2 + 32 + 1),
        ];
        for (args, ckb) in testcases.into_iter() {
            let script = packed::Script::new_builder().args(args.pack()).build();
            let expect = Capacity::bytes(ckb).unwrap();
            assert_eq!(script.occupied_capacity().unwrap(), expect);
        }
    }

    #[test]
    fn min_cell_output_capacity() {
        let lock = packed::Script::new_builder().build();
        let output = packed::CellOutput::new_builder().lock(lock).build();
        assert_eq!(
            output.occupied_capacity(Capacity::zero()).unwrap(),
            capacity_bytes!(41)
        );
    }

    #[test]
    fn min_secp256k1_cell_output_capacity() {
        let lock = packed::Script::new_builder()
            .args(vec![0u8; 20].pack())
            .build();
        let output = packed::CellOutput::new_builder().lock(lock).build();
        assert_eq!(
            output.occupied_capacity(Capacity::zero()).unwrap(),
            capacity_bytes!(61)
        );
    }
}