@@ -14,12 +14,10 @@ import {
14
14
Cell ,
15
15
core ,
16
16
} from "@ckb-lumos/lumos" ;
17
- import * as util from "@nervosnetwork/ckb-sdk-utils" ;
18
- import { Blake2b } from "@nervosnetwork/ckb-sdk-utils/lib/crypto/blake2b" ;
19
- import { SerializeRCData , SerializeRcLockWitnessLock } from "./generated/omni" ;
20
- import { H256 , Hasher , SparseMerkleTree } from "sparse-merkle-tree-ts" ;
21
- const { deploy } = commons ;
17
+ import { SerializeRcLockWitnessLock } from "./generated/omni" ;
18
+ import { generateSingleProof , ProofScheme , ProofMask , buildRcVec , h256ToHex , hexToH256 , H256 } from './lib' ;
22
19
const { Reader } = toolkit ;
20
+ const { deploy } = commons ;
23
21
24
22
export const CONFIG = config . createConfig ( {
25
23
PREFIX : "ckt" ,
@@ -67,65 +65,32 @@ const generateSECP256K1Account = (privKey: string): Account => {
67
65
} ;
68
66
69
67
export const alice = generateSECP256K1Account ( "0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245" ) ;
70
- export const bob = generateSECP256K1Account ( "0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245 " ) ;
68
+ export const bob = generateSECP256K1Account ( "0x5368b818f59570b5bc078a6a564f098a191dcb8938d95c413be5065fd6c42d32 " ) ;
71
69
72
70
export const rpc = new RPC ( CKB_RPC_URL ) ;
73
71
export const indexer = new Indexer ( CKB_INDEXER_URL , CKB_RPC_URL ) ;
74
72
73
+ export const deployRCEVecCell = async ( option : {
74
+ from : Address ;
75
+ fromPrivKey : HexString ;
76
+ rceCellHashs : HexString [ ] ;
77
+ } ) : Promise < config . ScriptConfig > => {
78
+ const rcData = buildRcVec ( option . rceCellHashs ) ;
75
79
76
- class Blake2bHasher extends Hasher {
77
- hasher : Blake2b ;
78
-
79
- constructor ( ) {
80
- super ( ) ;
81
-
82
- this . hasher = util . blake2b ( 32 , null , null , new TextEncoder ( ) . encode ( "ckb-default-hash" ) ) ;
83
- }
84
-
85
- update ( h : H256 ) : this {
86
- this . hasher . update ( h ) ;
87
-
88
- return this ;
89
- }
90
-
91
- final ( ) : H256 {
92
- return new H256 ( this . hasher . final ( "binary" ) as Uint8Array ) ;
93
- }
80
+ return deployRCECell ( { from : option . from , fromPrivKey : option . fromPrivKey , rcData } )
94
81
}
95
82
96
- const genSMT = ( items : [ H256 , H256 ] [ ] ) => {
97
- const tree = new SparseMerkleTree ( ( ) => new Blake2bHasher ( ) ) ;
98
-
99
- items . map ( ( item ) => {
100
- tree . update ( item [ 0 ] , item [ 1 ] ) ;
101
- } ) ;
102
-
103
- return tree ;
104
- } ;
105
-
106
- const buildRCETx = async ( fromAddress : Address , root : HexString ) => {
107
- const data = SerializeRCData ( {
108
- type : "RCRule" ,
109
- value : {
110
- smt_root : new Reader ( root ) . toArrayBuffer ( ) ,
111
- flags : 2 ,
112
- } ,
113
- } ) ;
114
-
115
- return deploy . generateDeployWithTypeIdTx ( {
116
- cellProvider : indexer ,
117
- scriptBinary : new Uint8Array ( data ) ,
118
- fromInfo : fromAddress ,
119
- config : CONFIG ,
120
- } ) ;
121
- } ;
122
-
123
- export const deployRCE = async ( option : {
83
+ export const deployRCECell = async ( option : {
124
84
from : Address ;
125
85
fromPrivKey : HexString ;
126
- root : HexString ;
86
+ rcData : H256 ;
127
87
} ) : Promise < config . ScriptConfig > => {
128
- const { txSkeleton, typeId : RCETypeScript } = await buildRCETx ( option . from , option . root ) ;
88
+ const { txSkeleton, typeId : RCETypeScript } = await deploy . generateDeployWithTypeIdTx ( {
89
+ cellProvider : indexer ,
90
+ scriptBinary : new Uint8Array ( option . rcData ) ,
91
+ fromInfo : option . from ,
92
+ config : CONFIG ,
93
+ } )
129
94
130
95
const tx = sealTxSkeleton ( txSkeleton , option . fromPrivKey ) ;
131
96
const txHash = await rpc . send_transaction ( tx ) ;
@@ -152,6 +117,7 @@ export function toMessages(tx: helpers.TransactionSkeletonType) {
152
117
153
118
// locks you want to sign
154
119
const signLock = tx . inputs . get ( 0 ) ?. cell_output . lock ! ;
120
+ console . log ( signLock )
155
121
156
122
const messageGroup = commons . createP2PKHMessageGroup ( tx , [ signLock ] , {
157
123
hasher : {
@@ -163,21 +129,55 @@ export function toMessages(tx: helpers.TransactionSkeletonType) {
163
129
return messageGroup [ 0 ] ;
164
130
}
165
131
132
+ interface SmtProof {
133
+ mask : HexString ,
134
+ proof : HexString ,
135
+ }
136
+
166
137
export const sealOmnilockTxSkeleton = (
167
- skeleton : helpers . TransactionSkeletonType ,
138
+ txSkeleton : helpers . TransactionSkeletonType ,
168
139
privateKey : string ,
169
140
identity : HexString ,
170
- proofs : HexString ,
141
+ proofs : Array < SmtProof > ,
171
142
) => {
172
- const messages = toMessages ( skeleton ) ;
143
+ const OMNI_SIGNATURE_PLACEHOLDER = new Reader (
144
+ "0x" +
145
+ "00" . repeat (
146
+ SerializeRcLockWitnessLock ( {
147
+ signature : new Reader ( "0x" + "00" . repeat ( 65 ) ) ,
148
+ rc_identity : {
149
+ identity : new Reader ( identity ) . toArrayBuffer ( ) ,
150
+ proofs : proofs . map ( p => ( {
151
+ mask : new Reader ( p . mask ) ,
152
+ proof : new Reader ( p . proof ) ,
153
+ } ) )
154
+ } ,
155
+ } ) . byteLength
156
+ )
157
+ ) ;
158
+
159
+ const newWitnessArgs = { lock : OMNI_SIGNATURE_PLACEHOLDER } ;
160
+ const witness = new Reader (
161
+ core . SerializeWitnessArgs ( toolkit . normalizers . NormalizeWitnessArgs ( newWitnessArgs ) )
162
+ ) . serializeJson ( ) ;
163
+
164
+ // fill txSkeleton's witness with 0
165
+ for ( let i = 0 ; i < txSkeleton . inputs . toArray ( ) . length ; i ++ ) {
166
+ txSkeleton = txSkeleton . update ( "witnesses" , ( witnesses ) => witnesses . push ( witness ) ) ;
167
+ }
168
+
169
+ const messages = toMessages ( txSkeleton ) ;
173
170
const signature = hd . key . signRecoverable ( messages . message , privateKey ) ;
174
171
175
172
const signedWitnessArgs = new Reader (
176
173
SerializeRcLockWitnessLock ( {
177
174
signature : new Reader ( signature ) ,
178
- omni_identity : {
179
- identity : new Reader ( identity ) ,
180
- proofs : new Reader ( proofs ) ,
175
+ rc_identity : {
176
+ identity : new Reader ( identity ) . toArrayBuffer ( ) ,
177
+ proofs : proofs . map ( p => ( {
178
+ mask : new Reader ( p . mask ) ,
179
+ proof : new Reader ( p . proof ) ,
180
+ } ) ) ,
181
181
} ,
182
182
} )
183
183
) ;
@@ -186,15 +186,15 @@ export const sealOmnilockTxSkeleton = (
186
186
core . SerializeWitnessArgs ( toolkit . normalizers . NormalizeWitnessArgs ( { lock : signedWitnessArgs } ) )
187
187
) . serializeJson ( ) ;
188
188
189
- const txSkeleton = skeleton . update ( "witnesses" , ( witnesses ) => witnesses . set ( 0 , signedWitness ) ) ;
189
+ txSkeleton = txSkeleton . update ( "witnesses" , ( witnesses ) => witnesses . set ( 0 , signedWitness ) ) ;
190
190
return helpers . createTransactionFromSkeleton ( txSkeleton ) ;
191
191
} ;
192
192
193
193
export interface TransferOptions {
194
194
from : string ;
195
195
to : string ;
196
196
amount : string ;
197
- rceCellConfig : config . ScriptConfig ;
197
+ rceCells : Array < config . ScriptConfig > ;
198
198
omniIdentity ?: {
199
199
identity : string ;
200
200
proofs : string ;
@@ -259,40 +259,17 @@ export async function buildTransferByOmnilockAdministrator(options: TransferOpti
259
259
} ,
260
260
dep_type : CONFIG . SCRIPTS . OMNI_LOCK . DEP_TYPE ,
261
261
} ,
262
- // RCE script
263
- {
264
- out_point : {
265
- tx_hash : options . rceCellConfig . TX_HASH ,
266
- index : options . rceCellConfig . INDEX ,
267
- } ,
268
- dep_type : options . rceCellConfig . DEP_TYPE ,
269
- }
270
- )
271
- ) ;
272
-
273
- const OMNI_SIGNATURE_PLACEHOLDER = new toolkit . Reader (
274
- "0x" +
275
- "00" . repeat (
276
- SerializeRcLockWitnessLock ( {
277
- signature : new toolkit . Reader ( "0x" + "00" . repeat ( 65 ) ) ,
278
- omni_identity : {
279
- identity : new toolkit . Reader ( "0x" + "00" . repeat ( 65 ) ) ,
280
- proofs : new toolkit . Reader ( "0x" + "00" . repeat ( 65 ) ) ,
262
+ // RCE cells
263
+ ...options . rceCells . map ( cell => ( {
264
+ out_point : {
265
+ tx_hash : cell . TX_HASH ,
266
+ index : cell . INDEX ,
281
267
} ,
282
- } ) . byteLength
283
- )
268
+ dep_type : cell . DEP_TYPE ,
269
+ } ) )
270
+ )
284
271
) ;
285
272
286
- const newWitnessArgs = { lock : OMNI_SIGNATURE_PLACEHOLDER } ;
287
- const witness = new toolkit . Reader (
288
- core . SerializeWitnessArgs ( toolkit . normalizers . NormalizeWitnessArgs ( newWitnessArgs ) )
289
- ) . serializeJson ( ) ;
290
-
291
- // fill txSkeleton's witness with 0
292
- for ( let i = 0 ; i < tx . inputs . toArray ( ) . length ; i ++ ) {
293
- tx = tx . update ( "witnesses" , ( witnesses ) => witnesses . push ( witness ) ) ;
294
- }
295
-
296
273
return tx ;
297
274
}
298
275
@@ -301,29 +278,94 @@ export const generateOmniLockAdministratorAddress = (pubHash: string, typeId: st
301
278
const lockScript : Script = {
302
279
code_hash : template . CODE_HASH ,
303
280
hash_type : template . HASH_TYPE ,
304
- // omni flag pubkey hash omni lock flags
305
- // chain identity eth addr function flag()
306
- // 00: Nervos 👇 00: owner
307
- // 01: Ethereum 👇 01: administrator
308
- // 👇 👇 👇
309
281
args : `0x00${ pubHash } 01${ typeId } ` ,
310
282
} ;
311
283
312
284
return helpers . encodeToAddress ( lockScript , { config : CONFIG } ) ;
313
285
}
314
286
315
- export const generateWLRule = ( wllist : Array < HexString > ) => {
316
- const auth_smt_keys = wllist . map ( wl => new H256 ( new Reader ( wl ) . toArrayBuffer ( ) ) ) ;
317
- let auth_smt_value = H256 . zero ( ) ;
318
- auth_smt_value [ 0 ] = 1 ;
319
- const smt = genSMT ( auth_smt_keys . map ( key => [ key , auth_smt_value ] ) ) ;
287
+ const main = async ( ) => {
288
+ const RC_IDENTITY = '0x00' + bob . lockScript . args . substring ( 2 ) ;
289
+ const { proof : wlProof , rcData : wlRcData } = generateSingleProof (
290
+ ProofScheme . OnWhiteList ,
291
+ [ hexToH256 ( RC_IDENTITY ) ]
292
+ )
293
+
294
+ const { proof : blProof , rcData : blRcData } = generateSingleProof (
295
+ ProofScheme . NotOnBlackList ,
296
+ [ hexToH256 ( RC_IDENTITY ) ]
297
+ )
298
+
299
+ // const wlRceCellScript = await deployRCECell({
300
+ // from: alice.address,
301
+ // fromPrivKey: alice.privKey,
302
+ // rcData: wlRcData,
303
+ // });
304
+
305
+ // generate by upper ↑
306
+ const wlRceCellScript : config . ScriptConfig = {
307
+ CODE_HASH : '0x7ff95669983cb7c54b8cb302f2f202a8d178c1b76f53c0fa76ba03ec97df6042' ,
308
+ HASH_TYPE : 'type' ,
309
+ TX_HASH : '0x584f8105aa6a39bd139a0f2321ea991b5033612f31339f5ddcf9eaf7e097af13' ,
310
+ INDEX : '0x0' ,
311
+ DEP_TYPE : 'code'
312
+ }
320
313
321
- const proof = smt . merkle_proof ( auth_smt_keys ) ;
322
- const compiledProof = proof . compile ( auth_smt_keys . map ( key => [ key , auth_smt_value ] ) ) ;
323
- const compiledProofHex = "0x" + compiledProof . map ( ( x ) => x . toString ( 16 ) . padStart ( 2 , "0" ) ) . join ( "" ) ;
314
+ // const blRceCellScript = await deployRCECell({
315
+ // from: alice.address,
316
+ // fromPrivKey: alice.privKey,
317
+ // rcData: blRcData,
318
+ // });
319
+
320
+ // generate by upper ↑
321
+ const blRceCellScript : config . ScriptConfig = {
322
+ CODE_HASH : '0x80a3aa808da4baf19728ba98297c3e96f10a6eccba7a10f66e35a8ef0845ab44' ,
323
+ HASH_TYPE : 'type' ,
324
+ TX_HASH : '0xa57484c8627a6c8f1ca47e95e2c1c26315154e2c5e34f0f02f4a9678411c889d' ,
325
+ INDEX : '0x0' ,
326
+ DEP_TYPE : 'code'
327
+ }
324
328
325
- return {
326
- root : new Reader ( smt . root ) . serializeJson ( ) ,
327
- proof : compiledProofHex ,
329
+ // const RceVecCellScript = await deployRCEVecCell({
330
+ // from: alice.address,
331
+ // fromPrivKey: alice.privKey,
332
+ // rceCellHashs: [wlRceCellScript.CODE_HASH, blRceCellScript.CODE_HASH],
333
+ // });
334
+
335
+ // generate by upper ↑
336
+ const RceVecCellScript : config . ScriptConfig = {
337
+ CODE_HASH : '0x23abc4961b1dfecc316f6cbf45e16da19faa50980ac6a8fd81e7df4211069945' ,
338
+ HASH_TYPE : 'type' ,
339
+ TX_HASH : '0x0d65b11ef71dcd7d41d3b3ec345fd41dcbebd555074419afe1511a94f31dc9c2' ,
340
+ INDEX : '0x0' ,
341
+ DEP_TYPE : 'code'
328
342
}
343
+
344
+ const finalCellScript : config . ScriptConfig = RceVecCellScript ;
345
+
346
+ const smtProofs = [ blProof , wlProof ] . map ( ( proof ) => ( {
347
+ mask : ProofMask . BothOn ,
348
+ proof : h256ToHex ( proof ) ,
349
+ } ) ) ;
350
+
351
+ const TYPE_ID = finalCellScript . CODE_HASH . substring ( 2 ) ;
352
+ const aliceOmniAddr = generateOmniLockAdministratorAddress ( alice . lockScript . args . substring ( 2 ) , TYPE_ID ) ;
353
+
354
+ const txSkeleton = await buildTransferByOmnilockAdministrator ( {
355
+ rceCells : [ wlRceCellScript , blRceCellScript , finalCellScript ] ,
356
+ from : aliceOmniAddr ,
357
+ to : bob . address ,
358
+ amount : "10000000000" ,
359
+ } ) ;
360
+
361
+ const signedTx = sealOmnilockTxSkeleton (
362
+ txSkeleton ,
363
+ bob . privKey ,
364
+ RC_IDENTITY ,
365
+ smtProofs ,
366
+ ) ;
367
+ const txHash = await rpc . send_transaction ( signedTx , "passthrough" ) ;
368
+ console . log ( txHash )
329
369
}
370
+
371
+ main ( )
0 commit comments