Skip to content

Commit d3d9125

Browse files
feat: unlock omnilock with rcecellvec
1 parent f8e056b commit d3d9125

File tree

2 files changed

+185
-218
lines changed

2 files changed

+185
-218
lines changed

examples/omni-lock-administrator/index.ts

+151-109
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ import {
1414
Cell,
1515
core,
1616
} 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';
2219
const { Reader } = toolkit;
20+
const { deploy } = commons;
2321

2422
export const CONFIG = config.createConfig({
2523
PREFIX: "ckt",
@@ -67,65 +65,32 @@ const generateSECP256K1Account = (privKey: string): Account => {
6765
};
6866

6967
export const alice = generateSECP256K1Account("0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245");
70-
export const bob = generateSECP256K1Account("0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245");
68+
export const bob = generateSECP256K1Account("0x5368b818f59570b5bc078a6a564f098a191dcb8938d95c413be5065fd6c42d32");
7169

7270
export const rpc = new RPC(CKB_RPC_URL);
7371
export const indexer = new Indexer(CKB_INDEXER_URL, CKB_RPC_URL);
7472

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);
7579

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 })
9481
}
9582

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: {
12484
from: Address;
12585
fromPrivKey: HexString;
126-
root: HexString;
86+
rcData: H256;
12787
}): 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+
})
12994

13095
const tx = sealTxSkeleton(txSkeleton, option.fromPrivKey);
13196
const txHash = await rpc.send_transaction(tx);
@@ -152,6 +117,7 @@ export function toMessages(tx: helpers.TransactionSkeletonType) {
152117

153118
// locks you want to sign
154119
const signLock = tx.inputs.get(0)?.cell_output.lock!;
120+
console.log(signLock)
155121

156122
const messageGroup = commons.createP2PKHMessageGroup(tx, [signLock], {
157123
hasher: {
@@ -163,21 +129,55 @@ export function toMessages(tx: helpers.TransactionSkeletonType) {
163129
return messageGroup[0];
164130
}
165131

132+
interface SmtProof {
133+
mask: HexString,
134+
proof: HexString,
135+
}
136+
166137
export const sealOmnilockTxSkeleton = (
167-
skeleton: helpers.TransactionSkeletonType,
138+
txSkeleton: helpers.TransactionSkeletonType,
168139
privateKey: string,
169140
identity: HexString,
170-
proofs: HexString,
141+
proofs: Array<SmtProof>,
171142
) => {
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);
173170
const signature = hd.key.signRecoverable(messages.message, privateKey);
174171

175172
const signedWitnessArgs = new Reader(
176173
SerializeRcLockWitnessLock({
177174
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+
})),
181181
},
182182
})
183183
);
@@ -186,15 +186,15 @@ export const sealOmnilockTxSkeleton = (
186186
core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs({ lock: signedWitnessArgs }))
187187
).serializeJson();
188188

189-
const txSkeleton = skeleton.update("witnesses", (witnesses) => witnesses.set(0, signedWitness));
189+
txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(0, signedWitness));
190190
return helpers.createTransactionFromSkeleton(txSkeleton);
191191
};
192192

193193
export interface TransferOptions {
194194
from: string;
195195
to: string;
196196
amount: string;
197-
rceCellConfig: config.ScriptConfig;
197+
rceCells: Array<config.ScriptConfig>;
198198
omniIdentity?: {
199199
identity: string;
200200
proofs: string;
@@ -259,40 +259,17 @@ export async function buildTransferByOmnilockAdministrator(options: TransferOpti
259259
},
260260
dep_type: CONFIG.SCRIPTS.OMNI_LOCK.DEP_TYPE,
261261
},
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,
281267
},
282-
}).byteLength
283-
)
268+
dep_type: cell.DEP_TYPE,
269+
}))
270+
)
284271
);
285272

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-
296273
return tx;
297274
}
298275

@@ -301,29 +278,94 @@ export const generateOmniLockAdministratorAddress = (pubHash: string, typeId: st
301278
const lockScript: Script = {
302279
code_hash: template.CODE_HASH,
303280
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-
// 👇 👇 👇
309281
args: `0x00${pubHash}01${typeId}`,
310282
};
311283

312284
return helpers.encodeToAddress(lockScript, { config: CONFIG });
313285
}
314286

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+
}
320313

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+
}
324328

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'
328342
}
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)
329369
}
370+
371+
main()

0 commit comments

Comments
 (0)