ckb_sdk/
constants.rs

1use std::convert::TryFrom;
2
3use crate::{CkbRpcAsyncClient, NetworkInfo, NetworkType, ScriptId};
4use ckb_system_scripts_v0_5_4::{
5    CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL as CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_LEGACY,
6    CODE_HASH_SECP256K1_DATA,
7};
8use ckb_system_scripts_v0_6_0::CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL as CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V2;
9use ckb_types::{
10    core::EpochNumberWithFraction,
11    h256,
12    packed::{Byte32, CellOutput, OutPoint, OutPointVecReader},
13    prelude::*,
14    H256,
15};
16
17pub const PREFIX_MAINNET: &str = "ckb";
18pub const PREFIX_TESTNET: &str = "ckt";
19
20pub const NETWORK_MAINNET: &str = "ckb";
21pub const NETWORK_TESTNET: &str = "ckb_testnet";
22pub const NETWORK_STAGING: &str = "ckb_staging";
23pub const NETWORK_PREVIEW: &str = "ckb_preview";
24pub const NETWORK_DEV: &str = "ckb_dev";
25
26pub const SECP_SIGNATURE_SIZE: usize = 65;
27
28// Since relative mask
29pub const LOCK_TYPE_FLAG: u64 = 1 << 63;
30pub const METRIC_TYPE_FLAG_MASK: u64 = 0x6000_0000_0000_0000;
31pub const VALUE_MASK: u64 = 0x00ff_ffff_ffff_ffff;
32pub const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000;
33
34// Special cells in genesis transactions: (transaction-index, output-index)
35pub const SIGHASH_OUTPUT_LOC: (usize, usize) = (0, 1);
36pub const MULTISIG_LEGACY_OUTPUT_LOC: (usize, usize) = (0, 4);
37pub const DAO_OUTPUT_LOC: (usize, usize) = (0, 2);
38pub const SIGHASH_GROUP_OUTPUT_LOC: (usize, usize) = (1, 0);
39pub const MULTISIG_LEGACY_GROUP_OUTPUT_LOC: (usize, usize) = (1, 1);
40
41pub const ONE_CKB: u64 = 100_000_000;
42pub const MIN_SECP_CELL_CAPACITY: u64 = 61 * ONE_CKB;
43// mainnet,testnet cellbase maturity
44pub const CELLBASE_MATURITY: EpochNumberWithFraction =
45    EpochNumberWithFraction::new_unchecked(4, 0, 1);
46
47/// "TYPE_ID" in hex (copied from ckb-chain-spec)
48pub const TYPE_ID_CODE_HASH: H256 = h256!("0x545950455f4944");
49
50pub const SIGHASH_TYPE_HASH: H256 =
51    h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8");
52
53pub const GENESIS_BLOCK_HASH_MAINNET: H256 =
54    h256!("0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5");
55
56pub const GENESIS_BLOCK_HASH_TESTNET: H256 =
57    h256!("0x10639e0895502b5688a6be8cf69460d76541bfa4821629d86d62ba0aae3f9606");
58
59pub const DAO_TYPE_HASH: H256 =
60    h256!("0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e");
61
62/// anyone can pay script mainnet code hash, see:
63/// <https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0026-anyone-can-pay/0026-anyone-can-pay.md#notes>
64pub const ACP_TYPE_HASH_LINA: H256 =
65    h256!("0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354");
66/// anyone can pay script testnet code hash
67pub const ACP_TYPE_HASH_AGGRON: H256 =
68    h256!("0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356");
69
70/// cheque withdraw since value
71pub const CHEQUE_CELL_SINCE: u64 = 0xA000000000000006;
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum MultisigScript {
75    /// Multisig Script deployed on Genesis Block
76    /// https://explorer.nervos.org/script/0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8/type
77    Legacy,
78
79    /// Latest multisig script
80    /// https://explorer.nervos.org/script/0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29/data1
81    V2,
82}
83
84impl MultisigScript {
85    pub const fn script_id(&self) -> ScriptId {
86        match self {
87            MultisigScript::Legacy => ScriptId::new_type(h256!(
88                "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8"
89            )),
90            MultisigScript::V2 => ScriptId::new_data1(h256!(
91                "0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29"
92            )),
93        }
94    }
95
96    fn dep_group_from_env(&self, _network: NetworkInfo) -> Option<(H256, u32)> {
97        let env_dep_group = match self {
98            MultisigScript::Legacy => std::env::var("MULTISIG_LEGACY_DEP_GROUP"),
99            MultisigScript::V2 => std::env::var("MULTISIG_V2_DEP_GROUP"),
100        }
101        .ok()?;
102
103        let vars = env_dep_group.split(",").collect::<Vec<_>>();
104        match (vars.first(), vars.get(1)) {
105            (Some(hash), Some(index)) => {
106                let index_u32: u32 = index.parse().ok()?;
107
108                if !hash.starts_with("0x") {
109                    return None;
110                }
111                let hash_bytes = hex::decode(&hash[2..]).ok()?;
112
113                let hash = H256::from_slice(&hash_bytes).ok()?;
114                Some((hash, index_u32))
115            }
116            _ => None,
117        }
118    }
119
120    /// Get dep group from env first:
121    /// 1. MULTISIG_LEGACY_DEP_GROUP=0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,1
122    /// 2. MULTISIG_V2_DEP_GROUP=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738,2
123    ///
124    /// If env not set, then get it from dep_group_inner
125    /// If genesis_block is None, it will fetch the genesis block from the network.
126    #[cfg(not(target_arch = "wasm32"))]
127    pub fn dep_group(
128        &self,
129        network: NetworkInfo,
130        genesis_block: Option<ckb_types::core::BlockView>,
131    ) -> Option<(H256, u32)> {
132        self.dep_group_from_env(network.clone())
133            .or(crate::rpc::block_on(
134                self.dep_group_inner(network, genesis_block),
135            ))
136    }
137
138    /// Get dep group from env first:
139    /// 1. MULTISIG_LEGACY_DEP_GROUP=0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,1
140    /// 2. MULTISIG_V2_DEP_GROUP=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738,2
141    ///
142    /// If env not set, then get it from dep_group_inner
143    /// If genesis_block is None, it will fetch the genesis block from the network.
144    pub async fn dep_group_async(
145        &self,
146        network: NetworkInfo,
147        genesis_block: Option<ckb_types::core::BlockView>,
148    ) -> Option<(H256, u32)> {
149        self.dep_group_from_env(network.clone())
150            .or(self.dep_group_inner(network, genesis_block).await)
151    }
152
153    async fn dep_group_inner(
154        &self,
155        network: NetworkInfo,
156        genesis_block: Option<ckb_types::core::BlockView>,
157    ) -> Option<(H256, u32)> {
158        match network.network_type {
159            NetworkType::Mainnet => Some(match self {
160                MultisigScript::Legacy => (
161                    h256!("0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"),
162                    1,
163                ),
164                MultisigScript::V2 => (
165                    h256!("0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738"),
166                    0,
167                ),
168            }),
169            NetworkType::Testnet => Some(match self {
170                MultisigScript::Legacy => (
171                    h256!("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"),
172                    1,
173                ),
174                MultisigScript::V2 => (
175                    h256!("0x2eefdeb21f3a3edf697c28a52601b4419806ed60bb427420455cc29a090b26d5"),
176                    0,
177                ),
178            }),
179            NetworkType::Staging | NetworkType::Preview | NetworkType::Dev => {
180                let genesis_block = if let Some(genesis_block) = genesis_block {
181                    genesis_block
182                } else {
183                    let client = CkbRpcAsyncClient::new(network.url.as_str());
184                    let json_genesis_block =
185                        client.get_block_by_number(0_u64.into()).await.ok()??;
186
187                    let genesis_block: ckb_types::core::BlockView = json_genesis_block.into();
188                    genesis_block
189                };
190
191                let secp256k1_data_outpoint =
192                    find_cell_match_data_hash(&genesis_block, CODE_HASH_SECP256K1_DATA.pack())?;
193
194                let target_data_hash = match self {
195                    MultisigScript::Legacy => {
196                        CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_LEGACY.pack()
197                    }
198                    MultisigScript::V2 => CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V2.pack(),
199                };
200                let multisig_outpoint =
201                    find_cell_match_data_hash(&genesis_block, target_data_hash)?;
202
203                let (dep_hash, dep_index) = find_cell_match_data_hash_find_dep(
204                    &genesis_block,
205                    vec![secp256k1_data_outpoint, multisig_outpoint],
206                )?;
207
208                let dep_hash: H256 = dep_hash.unpack();
209                Some((dep_hash, dep_index))
210            }
211        }
212    }
213}
214
215fn find_cell_match_data_hash(
216    genesis_block: &ckb_types::core::BlockView,
217    target_data_hash: Byte32,
218) -> Option<OutPoint> {
219    genesis_block.transactions().iter().find_map(|tx| {
220        let multisig_legacy_cell_index =
221            tx.outputs_with_data_iter()
222                .enumerate()
223                .find_map(|(index, (_output, data))| {
224                    let data_hash = CellOutput::calc_data_hash(&data);
225                    data_hash.eq(&target_data_hash).then_some(index)
226                });
227        multisig_legacy_cell_index.map(|cell_index| OutPoint::new(tx.hash(), cell_index as u32))
228    })
229}
230
231fn find_cell_match_data_hash_find_dep(
232    genesis_block: &ckb_types::core::BlockView,
233    target_points: Vec<OutPoint>,
234) -> Option<(ckb_types::packed::Byte32, u32)> {
235    genesis_block.transactions().iter().find_map(|tx| {
236        let multisig_cell_index: Option<u32> =
237            tx.outputs_with_data_iter()
238                .enumerate()
239                .find_map(|(index, (_output, data))| {
240                    let he = hex_string(&data);
241                    if he.len() > 200 {
242                        return None;
243                    }
244                    let outpoint_vec = OutPointVecReader::from_slice(&data)
245                        .map(|reader| reader.to_entity())
246                        .ok()?;
247
248                    target_points
249                        .iter()
250                        .all(|target_point| {
251                            outpoint_vec
252                                .clone()
253                                .into_iter()
254                                .any(|outpoint| outpoint.eq(target_point))
255                        })
256                        .then_some(index as u32)
257                });
258
259        multisig_cell_index.map(|cell_index| (tx.hash(), cell_index))
260    })
261}
262
263impl TryFrom<H256> for MultisigScript {
264    type Error = ();
265
266    fn try_from(code_hash: H256) -> Result<Self, Self::Error> {
267        if code_hash.eq(&MultisigScript::Legacy.script_id().code_hash) {
268            Ok(MultisigScript::Legacy)
269        } else if code_hash.eq(&MultisigScript::V2.script_id().code_hash) {
270            Ok(MultisigScript::V2)
271        } else {
272            Err(())
273        }
274    }
275}
276
277#[cfg(test)]
278mod test {
279    use super::*;
280    use ckb_types::{
281        core::Capacity,
282        packed::{CellOutput, Script},
283        H160,
284    };
285
286    #[test]
287    fn test_min_capacity() {
288        let min_secp_cell_capacity = CellOutput::new_builder()
289            .lock(
290                Script::new_builder()
291                    .args(H160::default().as_bytes().pack())
292                    .build(),
293            )
294            .build()
295            .occupied_capacity(Capacity::zero())
296            .unwrap()
297            .as_u64();
298
299        assert_eq!(min_secp_cell_capacity, MIN_SECP_CELL_CAPACITY);
300    }
301
302    #[test]
303    fn test_multisig_deps() {
304        assert_ne!(
305            CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_LEGACY,
306            CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V2
307        );
308
309        let mainnet = NetworkInfo::mainnet();
310        assert!(MultisigScript::Legacy
311            .dep_group(mainnet.clone(), None)
312            .is_some());
313        assert!(MultisigScript::V2.dep_group(mainnet, None).is_some());
314
315        let testnet = NetworkInfo::testnet();
316        assert!(MultisigScript::Legacy
317            .dep_group(testnet.clone(), None)
318            .is_some());
319        assert!(MultisigScript::V2.dep_group(testnet, None).is_some());
320
321        // TODO: start ckb devchain in this unit test
322        // let devnet = NetworkInfo::devnet();
323        // assert!(MultisigScript::Legacy.dep_group(devnet.clone()).is_some());
324
325        // TODO, let ckb devnet deploy multisig_v2 on genesis block
326        // assert!(MultisigScript::V1.dep_group(devnet).is_some());
327    }
328
329    #[test]
330    fn test_dep_group_from_env() {
331        let legacy = MultisigScript::Legacy;
332        std::env::set_var(
333            "MULTISIG_LEGACY_DEP_GROUP",
334            "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,10000",
335        );
336        let dep_group = legacy.dep_group_from_env(NetworkInfo::devnet());
337        assert!(dep_group.is_some());
338        assert_eq!(dep_group.unwrap().1, 10000)
339    }
340}