ckb_dao_utils/
lib.rs

1//! This crate provides several util functions to operate the dao field and NervosDAO related errors.
2
3mod error;
4
5use byteorder::{ByteOrder, LittleEndian};
6use ckb_types::{
7    H160,
8    core::{Capacity, Ratio, TransactionView, capacity_bytes},
9    packed::{Byte32, OutPoint},
10    prelude::*,
11};
12use std::collections::HashSet;
13
14pub use crate::error::DaoError;
15
16// This is multiplied by 10**16 to make sure we have enough precision.
17const DEFAULT_GENESIS_ACCUMULATE_RATE: u64 = 10_000_000_000_000_000;
18
19/// Calculate the dao field for the genesis block.
20///
21/// NOTE: Used for testing only!
22#[doc(hidden)]
23pub fn genesis_dao_data(txs: Vec<&TransactionView>) -> Result<Byte32, DaoError> {
24    genesis_dao_data_with_satoshi_gift(
25        txs,
26        &H160([0u8; 20]),
27        Ratio::new(1, 1),
28        capacity_bytes!(1_000_000),
29        capacity_bytes!(1000),
30    )
31}
32
33/// Calculate the dao field for the genesis block.
34#[doc(hidden)]
35pub fn genesis_dao_data_with_satoshi_gift(
36    txs: Vec<&TransactionView>,
37    satoshi_pubkey_hash: &H160,
38    satoshi_cell_occupied_ratio: Ratio,
39    initial_primary_issuance: Capacity,
40    initial_secondary_issuance: Capacity,
41) -> Result<Byte32, DaoError> {
42    let dead_cells = txs
43        .iter()
44        .flat_map(|tx| tx.inputs().into_iter().map(|input| input.previous_output()))
45        .collect::<HashSet<_>>();
46    let statistics_outputs = |tx_index, tx: &TransactionView| -> Result<_, DaoError> {
47        let c = tx
48            .data()
49            .raw()
50            .outputs()
51            .into_iter()
52            .enumerate()
53            .filter(|(index, _)| !dead_cells.contains(&OutPoint::new(tx.hash(), *index as u32)))
54            .try_fold(Capacity::zero(), |capacity, (_, output)| {
55                let cap: Capacity = output.capacity().into();
56                capacity.safe_add(cap)
57            })?;
58        let u = tx
59            .outputs_with_data_iter()
60            .enumerate()
61            .filter(|(index, _)| !dead_cells.contains(&OutPoint::new(tx.hash(), *index as u32)))
62            .try_fold(Capacity::zero(), |capacity, (_, (output, data))| {
63                // detect satoshi gift cell
64                let occupied_capacity = if tx_index == 0
65                    && output.lock().args().raw_data() == satoshi_pubkey_hash.0[..]
66                {
67                    Into::<Capacity>::into(output.capacity())
68                        .safe_mul_ratio(satoshi_cell_occupied_ratio)
69                } else {
70                    Capacity::bytes(data.len()).and_then(|c| output.occupied_capacity(c))
71                };
72                occupied_capacity.and_then(|c| capacity.safe_add(c))
73            })?;
74        Ok((c, u))
75    };
76
77    let initial_issuance = initial_primary_issuance.safe_add(initial_secondary_issuance)?;
78    let result: Result<_, DaoError> = txs.into_iter().enumerate().try_fold(
79        (initial_issuance, Capacity::zero()),
80        |(c, u), (tx_index, tx)| {
81            let (tx_c, tx_u) = statistics_outputs(tx_index, tx)?;
82            let c = c.safe_add(tx_c)?;
83            let u = u.safe_add(tx_u)?;
84            Ok((c, u))
85        },
86    );
87    let (c, u) = result?;
88    // C cannot be zero, otherwise DAO stats calculation might result in
89    // division by zero errors.
90    if c == Capacity::zero() {
91        return Err(DaoError::ZeroC);
92    }
93    Ok(pack_dao_data(
94        DEFAULT_GENESIS_ACCUMULATE_RATE,
95        c,
96        initial_secondary_issuance,
97        u,
98    ))
99}
100
101/// Extract `ar`, `c`, `s`, and `u` from [`Byte32`].
102///
103/// [`Byte32`]: ../ckb_types/packed/struct.Byte32.html
104pub fn extract_dao_data(dao: Byte32) -> (u64, Capacity, Capacity, Capacity) {
105    let data = dao.raw_data();
106    let c = Capacity::shannons(LittleEndian::read_u64(&data[0..8]));
107    let ar = LittleEndian::read_u64(&data[8..16]);
108    let s = Capacity::shannons(LittleEndian::read_u64(&data[16..24]));
109    let u = Capacity::shannons(LittleEndian::read_u64(&data[24..32]));
110    (ar, c, s, u)
111}
112
113/// Pack `ar`, `c`, `s`, and `u` into [`Byte32`] in little endian.
114///
115/// [`Byte32`]: ../ckb_types/packed/struct.Byte32.html
116pub fn pack_dao_data(ar: u64, c: Capacity, s: Capacity, u: Capacity) -> Byte32 {
117    let mut buf = [0u8; 32];
118    LittleEndian::write_u64(&mut buf[0..8], c.as_u64());
119    LittleEndian::write_u64(&mut buf[8..16], ar);
120    LittleEndian::write_u64(&mut buf[16..24], s.as_u64());
121    LittleEndian::write_u64(&mut buf[24..32], u.as_u64());
122    Byte32::from_slice(&buf).expect("impossible: fail to read array")
123}
124
125#[cfg(test)]
126mod tests {
127    pub use super::{extract_dao_data, pack_dao_data};
128    pub use ckb_types::core::Capacity;
129    pub use ckb_types::h256;
130    pub use ckb_types::packed::Byte32;
131
132    #[test]
133    #[allow(clippy::unreadable_literal)]
134    fn test_dao_data() {
135        let cases = vec![
136            (
137                // mainnet block[0]
138                h256!("0x8874337e541ea12e0000c16ff286230029bfa3320800000000710b00c0fefe06"),
139                10000000000000000,
140                Capacity::shannons(3360000145238488200),
141                Capacity::shannons(35209330473),
142                Capacity::shannons(504120308900000000),
143            ),
144            (
145                // mainnet block[1]
146                h256!("0x10e9164f761ea12ea5f6ff75f28623007b7f682a0f00000000710b00c0fefe06"),
147                10000000104789669,
148                Capacity::shannons(3360000290476976400),
149                Capacity::shannons(65136000891),
150                Capacity::shannons(504120308900000000),
151            ),
152            (
153                // mainnet block[5892]
154                h256!("0x95b47fdcff26a42ed0fb76e081872300bb585ebd10a000000043c2f76b5eff06"),
155                10000616071298000,
156                Capacity::shannons(3360854102283105429),
157                Capacity::shannons(175993756997819),
158                Capacity::shannons(504225501100000000),
159            ),
160        ];
161        for (dao_h256, ar, c, s, u) in cases {
162            let dao_byte32: Byte32 = dao_h256.into();
163            assert_eq!((ar, c, s, u), extract_dao_data(dao_byte32.clone()));
164            assert_eq!(dao_byte32, pack_dao_data(ar, c, s, u,));
165        }
166    }
167}