1- use ckb_types:: { core:: EpochNumberWithFraction , h256, H256 } ;
1+ use std:: convert:: TryFrom ;
2+
3+ use crate :: { CkbRpcClient , NetworkInfo , NetworkType , ScriptId } ;
4+ use 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+ } ;
8+ use ckb_system_scripts_v0_6_0:: CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL as CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V1 ;
9+ use ckb_types:: {
10+ core:: EpochNumberWithFraction ,
11+ h256,
12+ packed:: { Byte32 , CellOutput , OutPoint , OutPointVecReader } ,
13+ prelude:: * ,
14+ H256 ,
15+ } ;
216
317pub const PREFIX_MAINNET : & str = "ckb" ;
418pub const PREFIX_TESTNET : & str = "ckt" ;
@@ -19,10 +33,10 @@ pub const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000;
1933
2034// Special cells in genesis transactions: (transaction-index, output-index)
2135pub const SIGHASH_OUTPUT_LOC : ( usize , usize ) = ( 0 , 1 ) ;
22- pub const MULTISIG_OUTPUT_LOC : ( usize , usize ) = ( 0 , 4 ) ;
36+ pub const MULTISIG_LEGACY_OUTPUT_LOC : ( usize , usize ) = ( 0 , 4 ) ;
2337pub const DAO_OUTPUT_LOC : ( usize , usize ) = ( 0 , 2 ) ;
2438pub const SIGHASH_GROUP_OUTPUT_LOC : ( usize , usize ) = ( 1 , 0 ) ;
25- pub const MULTISIG_GROUP_OUTPUT_LOC : ( usize , usize ) = ( 1 , 1 ) ;
39+ pub const MULTISIG_LEGACY_GROUP_OUTPUT_LOC : ( usize , usize ) = ( 1 , 1 ) ;
2640
2741pub const ONE_CKB : u64 = 100_000_000 ;
2842pub const MIN_SECP_CELL_CAPACITY : u64 = 61 * ONE_CKB ;
@@ -35,8 +49,13 @@ pub const TYPE_ID_CODE_HASH: H256 = h256!("0x545950455f4944");
3549
3650pub const SIGHASH_TYPE_HASH : H256 =
3751 h256 ! ( "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" ) ;
38- pub const MULTISIG_TYPE_HASH : H256 =
39- h256 ! ( "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8" ) ;
52+
53+ pub const GENESIS_BLOCK_HASH_MAINNET : H256 =
54+ h256 ! ( "0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5" ) ;
55+
56+ pub const GENESIS_BLOCK_HASH_TESTNET : H256 =
57+ h256 ! ( "0x10639e0895502b5688a6be8cf69460d76541bfa4821629d86d62ba0aae3f9606" ) ;
58+
4059pub const DAO_TYPE_HASH : H256 =
4160 h256 ! ( "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e" ) ;
4261
@@ -51,13 +70,182 @@ pub const ACP_TYPE_HASH_AGGRON: H256 =
5170/// cheque withdraw since value
5271pub const CHEQUE_CELL_SINCE : u64 = 0xA000000000000006 ;
5372
73+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
74+ pub 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+ V1 ,
82+ }
83+
84+ impl MultisigScript {
85+ pub const fn script_id ( & self ) -> ScriptId {
86+ match self {
87+ MultisigScript :: Legacy => ScriptId :: new_type ( h256 ! (
88+ "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8"
89+ ) ) ,
90+ MultisigScript :: V1 => 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 :: V1 => std:: env:: var ( "MULTISIG_V1_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_V1_DEP_GROUP=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738,2
123+ ///
124+ /// If env not set, then get it from dep_group_inner
125+ pub fn dep_group ( & self , network : NetworkInfo ) -> Option < ( H256 , u32 ) > {
126+ self . dep_group_from_env ( network. clone ( ) )
127+ . or ( self . dep_group_inner ( network) )
128+ }
129+
130+ pub fn dep_group_inner ( & self , network : NetworkInfo ) -> Option < ( H256 , u32 ) > {
131+ match network. network_type {
132+ NetworkType :: Mainnet => Some ( match self {
133+ MultisigScript :: Legacy => (
134+ h256 ! ( "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c" ) ,
135+ 1 ,
136+ ) ,
137+ MultisigScript :: V1 => (
138+ h256 ! ( "0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738" ) ,
139+ 0 ,
140+ ) ,
141+ } ) ,
142+ NetworkType :: Testnet => Some ( match self {
143+ MultisigScript :: Legacy => (
144+ h256 ! ( "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37" ) ,
145+ 1 ,
146+ ) ,
147+ MultisigScript :: V1 => (
148+ h256 ! ( "0x2eefdeb21f3a3edf697c28a52601b4419806ed60bb427420455cc29a090b26d5" ) ,
149+ 0 ,
150+ ) ,
151+ } ) ,
152+ NetworkType :: Staging | NetworkType :: Preview | NetworkType :: Dev => {
153+ let client = CkbRpcClient :: new ( network. url . as_str ( ) ) ;
154+ let json_genesis_block = client. get_block_by_number ( 0_u64 . into ( ) ) . ok ( ) ??;
155+ let genesis_block: ckb_types:: core:: BlockView = json_genesis_block. into ( ) ;
156+
157+ let secp256k1_data_outpoint =
158+ find_cell_match_data_hash ( & genesis_block, CODE_HASH_SECP256K1_DATA . pack ( ) ) ?;
159+
160+ let target_data_hash = match self {
161+ MultisigScript :: Legacy => {
162+ CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_LEGACY . pack ( )
163+ }
164+ MultisigScript :: V1 => CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V1 . pack ( ) ,
165+ } ;
166+ let multisig_outpoint =
167+ find_cell_match_data_hash ( & genesis_block, target_data_hash) ?;
168+
169+ let ( dep_hash, dep_index) = find_cell_match_data_hash_find_dep (
170+ & genesis_block,
171+ vec ! [ secp256k1_data_outpoint, multisig_outpoint] ,
172+ ) ?;
173+
174+ let dep_hash: H256 = dep_hash. unpack ( ) ;
175+ Some ( ( dep_hash, dep_index) )
176+ }
177+ }
178+ }
179+ }
180+
181+ fn find_cell_match_data_hash (
182+ genesis_block : & ckb_types:: core:: BlockView ,
183+ target_data_hash : Byte32 ,
184+ ) -> Option < OutPoint > {
185+ genesis_block. transactions ( ) . iter ( ) . find_map ( |tx| {
186+ let multisig_legacy_cell_index =
187+ tx. outputs_with_data_iter ( )
188+ . enumerate ( )
189+ . find_map ( |( index, ( _output, data) ) | {
190+ let data_hash = CellOutput :: calc_data_hash ( & data) ;
191+ data_hash. eq ( & target_data_hash) . then_some ( index)
192+ } ) ;
193+ multisig_legacy_cell_index. map ( |cell_index| OutPoint :: new ( tx. hash ( ) , cell_index as u32 ) )
194+ } )
195+ }
196+
197+ fn find_cell_match_data_hash_find_dep (
198+ genesis_block : & ckb_types:: core:: BlockView ,
199+ target_points : Vec < OutPoint > ,
200+ ) -> Option < ( ckb_types:: packed:: Byte32 , u32 ) > {
201+ genesis_block. transactions ( ) . iter ( ) . find_map ( |tx| {
202+ let multisig_cell_index: Option < u32 > =
203+ tx. outputs_with_data_iter ( )
204+ . enumerate ( )
205+ . find_map ( |( index, ( _output, data) ) | {
206+ let he = hex_string ( & data) ;
207+ if he. len ( ) > 200 {
208+ return None ;
209+ }
210+ let outpoint_vec = OutPointVecReader :: from_slice ( & data)
211+ . map ( |reader| reader. to_entity ( ) )
212+ . ok ( ) ?;
213+
214+ target_points
215+ . iter ( )
216+ . all ( |target_point| {
217+ outpoint_vec
218+ . clone ( )
219+ . into_iter ( )
220+ . any ( |outpoint| outpoint. eq ( target_point) )
221+ } )
222+ . then_some ( index as u32 )
223+ } ) ;
224+
225+ multisig_cell_index. map ( |cell_index| ( tx. hash ( ) , cell_index) )
226+ } )
227+ }
228+
229+ impl TryFrom < H256 > for MultisigScript {
230+ type Error = ( ) ;
231+
232+ fn try_from ( code_hash : H256 ) -> Result < Self , Self :: Error > {
233+ if code_hash. eq ( & MultisigScript :: Legacy . script_id ( ) . code_hash ) {
234+ Ok ( MultisigScript :: Legacy )
235+ } else if code_hash. eq ( & MultisigScript :: V1 . script_id ( ) . code_hash ) {
236+ Ok ( MultisigScript :: V1 )
237+ } else {
238+ Err ( ( ) )
239+ }
240+ }
241+ }
242+
54243#[ cfg( test) ]
55244mod test {
56245 use super :: * ;
57246 use ckb_types:: {
58247 core:: Capacity ,
59248 packed:: { CellOutput , Script } ,
60- prelude:: * ,
61249 H160 ,
62250 } ;
63251
@@ -76,4 +264,39 @@ mod test {
76264
77265 assert_eq ! ( min_secp_cell_capacity, MIN_SECP_CELL_CAPACITY ) ;
78266 }
267+
268+ #[ test]
269+ fn test_multisig_deps ( ) {
270+ assert_ne ! (
271+ CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_LEGACY ,
272+ CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL_V1
273+ ) ;
274+
275+ let mainnet = NetworkInfo :: mainnet ( ) ;
276+ assert ! ( MultisigScript :: Legacy . dep_group( mainnet. clone( ) ) . is_some( ) ) ;
277+ assert ! ( MultisigScript :: V1 . dep_group( mainnet) . is_some( ) ) ;
278+
279+ let testnet = NetworkInfo :: testnet ( ) ;
280+ assert ! ( MultisigScript :: Legacy . dep_group( testnet. clone( ) ) . is_some( ) ) ;
281+ assert ! ( MultisigScript :: V1 . dep_group( testnet) . is_some( ) ) ;
282+
283+ // TODO: start ckb devchain in this unit test
284+ // let devnet = NetworkInfo::devnet();
285+ // assert!(MultisigScript::Legacy.dep_group(devnet.clone()).is_some());
286+
287+ // TODO, let ckb devnet deploy multisig_v1 on genesis block
288+ // assert!(MultisigScript::V1.dep_group(devnet).is_some());
289+ }
290+
291+ #[ test]
292+ fn test_dep_group_from_env ( ) {
293+ let legacy = MultisigScript :: Legacy ;
294+ std:: env:: set_var (
295+ "MULTISIG_LEGACY_DEP_GROUP" ,
296+ "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,10000" ,
297+ ) ;
298+ let dep_group = legacy. dep_group_from_env ( NetworkInfo :: devnet ( ) ) ;
299+ assert ! ( dep_group. is_some( ) ) ;
300+ assert_eq ! ( dep_group. unwrap( ) . 1 , 10000 )
301+ }
79302}
0 commit comments