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
28pub 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
34pub 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;
43pub const CELLBASE_MATURITY: EpochNumberWithFraction =
45 EpochNumberWithFraction::new_unchecked(4, 0, 1);
46
47pub 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
62pub const ACP_TYPE_HASH_LINA: H256 =
65 h256!("0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354");
66pub const ACP_TYPE_HASH_AGGRON: H256 =
68 h256!("0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356");
69
70pub const CHEQUE_CELL_SINCE: u64 = 0xA000000000000006;
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum MultisigScript {
75 Legacy,
78
79 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 #[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 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 }
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}