Skip to content

Commit

Permalink
feat: unlock omnilock with rcecellvec
Browse files Browse the repository at this point in the history
  • Loading branch information
PainterPuppets committed Jun 23, 2022
1 parent f8e056b commit d3d9125
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 218 deletions.
260 changes: 151 additions & 109 deletions examples/omni-lock-administrator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ import {
Cell,
core,
} from "@ckb-lumos/lumos";
import * as util from "@nervosnetwork/ckb-sdk-utils";
import { Blake2b } from "@nervosnetwork/ckb-sdk-utils/lib/crypto/blake2b";
import { SerializeRCData, SerializeRcLockWitnessLock } from "./generated/omni";
import { H256, Hasher, SparseMerkleTree } from "sparse-merkle-tree-ts";
const { deploy } = commons;
import { SerializeRcLockWitnessLock } from "./generated/omni";
import { generateSingleProof, ProofScheme, ProofMask, buildRcVec, h256ToHex, hexToH256, H256 } from './lib';
const { Reader } = toolkit;
const { deploy } = commons;

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

export const alice = generateSECP256K1Account("0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245");
export const bob = generateSECP256K1Account("0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245");
export const bob = generateSECP256K1Account("0x5368b818f59570b5bc078a6a564f098a191dcb8938d95c413be5065fd6c42d32");

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

export const deployRCEVecCell = async (option: {
from: Address;
fromPrivKey: HexString;
rceCellHashs: HexString[];
}): Promise<config.ScriptConfig> => {
const rcData = buildRcVec(option.rceCellHashs);

class Blake2bHasher extends Hasher {
hasher: Blake2b;

constructor() {
super();

this.hasher = util.blake2b(32, null, null, new TextEncoder().encode("ckb-default-hash"));
}

update(h: H256): this {
this.hasher.update(h);

return this;
}

final(): H256 {
return new H256(this.hasher.final("binary") as Uint8Array);
}
return deployRCECell({ from: option.from, fromPrivKey: option.fromPrivKey, rcData })
}

const genSMT = (items: [H256, H256][]) => {
const tree = new SparseMerkleTree(() => new Blake2bHasher());

items.map((item) => {
tree.update(item[0], item[1]);
});

return tree;
};

const buildRCETx = async (fromAddress: Address, root: HexString) => {
const data = SerializeRCData({
type: "RCRule",
value: {
smt_root: new Reader(root).toArrayBuffer(),
flags: 2,
},
});

return deploy.generateDeployWithTypeIdTx({
cellProvider: indexer,
scriptBinary: new Uint8Array(data),
fromInfo: fromAddress,
config: CONFIG,
});
};

export const deployRCE = async (option: {
export const deployRCECell = async (option: {
from: Address;
fromPrivKey: HexString;
root: HexString;
rcData: H256;
}): Promise<config.ScriptConfig> => {
const { txSkeleton, typeId: RCETypeScript } = await buildRCETx(option.from, option.root);
const { txSkeleton, typeId: RCETypeScript } = await deploy.generateDeployWithTypeIdTx({
cellProvider: indexer,
scriptBinary: new Uint8Array(option.rcData),
fromInfo: option.from,
config: CONFIG,
})

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

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

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

interface SmtProof {
mask: HexString,
proof: HexString,
}

export const sealOmnilockTxSkeleton = (
skeleton: helpers.TransactionSkeletonType,
txSkeleton: helpers.TransactionSkeletonType,
privateKey: string,
identity: HexString,
proofs: HexString,
proofs: Array<SmtProof>,
) => {
const messages = toMessages(skeleton);
const OMNI_SIGNATURE_PLACEHOLDER = new Reader(
"0x" +
"00".repeat(
SerializeRcLockWitnessLock({
signature: new Reader("0x" + "00".repeat(65)),
rc_identity: {
identity: new Reader(identity).toArrayBuffer(),
proofs: proofs.map(p => ({
mask: new Reader(p.mask),
proof: new Reader(p.proof),
}))
},
}).byteLength
)
);

const newWitnessArgs = { lock: OMNI_SIGNATURE_PLACEHOLDER };
const witness = new Reader(
core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs))
).serializeJson();

// fill txSkeleton's witness with 0
for (let i = 0; i < txSkeleton.inputs.toArray().length; i++) {
txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.push(witness));
}

const messages = toMessages(txSkeleton);
const signature = hd.key.signRecoverable(messages.message, privateKey);

const signedWitnessArgs = new Reader(
SerializeRcLockWitnessLock({
signature: new Reader(signature),
omni_identity: {
identity: new Reader(identity),
proofs: new Reader(proofs),
rc_identity: {
identity: new Reader(identity).toArrayBuffer(),
proofs: proofs.map(p => ({
mask: new Reader(p.mask),
proof: new Reader(p.proof),
})),
},
})
);
Expand All @@ -186,15 +186,15 @@ export const sealOmnilockTxSkeleton = (
core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs({ lock: signedWitnessArgs }))
).serializeJson();

const txSkeleton = skeleton.update("witnesses", (witnesses) => witnesses.set(0, signedWitness));
txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(0, signedWitness));
return helpers.createTransactionFromSkeleton(txSkeleton);
};

export interface TransferOptions {
from: string;
to: string;
amount: string;
rceCellConfig: config.ScriptConfig;
rceCells: Array<config.ScriptConfig>;
omniIdentity?: {
identity: string;
proofs: string;
Expand Down Expand Up @@ -259,40 +259,17 @@ export async function buildTransferByOmnilockAdministrator(options: TransferOpti
},
dep_type: CONFIG.SCRIPTS.OMNI_LOCK.DEP_TYPE,
},
// RCE script
{
out_point: {
tx_hash: options.rceCellConfig.TX_HASH,
index: options.rceCellConfig.INDEX,
},
dep_type: options.rceCellConfig.DEP_TYPE,
}
)
);

const OMNI_SIGNATURE_PLACEHOLDER = new toolkit.Reader(
"0x" +
"00".repeat(
SerializeRcLockWitnessLock({
signature: new toolkit.Reader("0x" + "00".repeat(65)),
omni_identity: {
identity: new toolkit.Reader("0x" + "00".repeat(65)),
proofs: new toolkit.Reader("0x" + "00".repeat(65)),
// RCE cells
...options.rceCells.map(cell => ({
out_point: {
tx_hash: cell.TX_HASH,
index: cell.INDEX,
},
}).byteLength
)
dep_type: cell.DEP_TYPE,
}))
)
);

const newWitnessArgs = { lock: OMNI_SIGNATURE_PLACEHOLDER };
const witness = new toolkit.Reader(
core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs))
).serializeJson();

// fill txSkeleton's witness with 0
for (let i = 0; i < tx.inputs.toArray().length; i++) {
tx = tx.update("witnesses", (witnesses) => witnesses.push(witness));
}

return tx;
}

Expand All @@ -301,29 +278,94 @@ export const generateOmniLockAdministratorAddress = (pubHash: string, typeId: st
const lockScript: Script = {
code_hash: template.CODE_HASH,
hash_type: template.HASH_TYPE,
// omni flag pubkey hash omni lock flags
// chain identity eth addr function flag()
// 00: Nervos 👇 00: owner
// 01: Ethereum 👇 01: administrator
// 👇 👇 👇
args: `0x00${pubHash}01${typeId}`,
};

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

export const generateWLRule = (wllist: Array<HexString>) => {
const auth_smt_keys = wllist.map(wl => new H256(new Reader(wl).toArrayBuffer()));
let auth_smt_value = H256.zero();
auth_smt_value[0] = 1;
const smt = genSMT(auth_smt_keys.map(key => [key, auth_smt_value]));
const main = async () => {
const RC_IDENTITY = '0x00' + bob.lockScript.args.substring(2);
const { proof: wlProof, rcData: wlRcData } = generateSingleProof(
ProofScheme.OnWhiteList,
[hexToH256(RC_IDENTITY)]
)

const { proof: blProof, rcData: blRcData } = generateSingleProof(
ProofScheme.NotOnBlackList,
[hexToH256(RC_IDENTITY)]
)

// const wlRceCellScript = await deployRCECell({
// from: alice.address,
// fromPrivKey: alice.privKey,
// rcData: wlRcData,
// });

// generate by upper ↑
const wlRceCellScript: config.ScriptConfig = {
CODE_HASH: '0x7ff95669983cb7c54b8cb302f2f202a8d178c1b76f53c0fa76ba03ec97df6042',
HASH_TYPE: 'type',
TX_HASH: '0x584f8105aa6a39bd139a0f2321ea991b5033612f31339f5ddcf9eaf7e097af13',
INDEX: '0x0',
DEP_TYPE: 'code'
}

const proof = smt.merkle_proof(auth_smt_keys);
const compiledProof = proof.compile(auth_smt_keys.map(key => [key, auth_smt_value]));
const compiledProofHex = "0x" + compiledProof.map((x) => x.toString(16).padStart(2, "0")).join("");
// const blRceCellScript = await deployRCECell({
// from: alice.address,
// fromPrivKey: alice.privKey,
// rcData: blRcData,
// });

// generate by upper ↑
const blRceCellScript: config.ScriptConfig = {
CODE_HASH: '0x80a3aa808da4baf19728ba98297c3e96f10a6eccba7a10f66e35a8ef0845ab44',
HASH_TYPE: 'type',
TX_HASH: '0xa57484c8627a6c8f1ca47e95e2c1c26315154e2c5e34f0f02f4a9678411c889d',
INDEX: '0x0',
DEP_TYPE: 'code'
}

return {
root: new Reader(smt.root).serializeJson(),
proof: compiledProofHex,
// const RceVecCellScript = await deployRCEVecCell({
// from: alice.address,
// fromPrivKey: alice.privKey,
// rceCellHashs: [wlRceCellScript.CODE_HASH, blRceCellScript.CODE_HASH],
// });

// generate by upper ↑
const RceVecCellScript: config.ScriptConfig = {
CODE_HASH: '0x23abc4961b1dfecc316f6cbf45e16da19faa50980ac6a8fd81e7df4211069945',
HASH_TYPE: 'type',
TX_HASH: '0x0d65b11ef71dcd7d41d3b3ec345fd41dcbebd555074419afe1511a94f31dc9c2',
INDEX: '0x0',
DEP_TYPE: 'code'
}

const finalCellScript: config.ScriptConfig = RceVecCellScript;

const smtProofs = [blProof, wlProof].map((proof) => ({
mask: ProofMask.BothOn,
proof: h256ToHex(proof),
}));

const TYPE_ID = finalCellScript.CODE_HASH.substring(2);
const aliceOmniAddr = generateOmniLockAdministratorAddress(alice.lockScript.args.substring(2), TYPE_ID);

const txSkeleton = await buildTransferByOmnilockAdministrator({
rceCells: [wlRceCellScript, blRceCellScript, finalCellScript],
from: aliceOmniAddr,
to: bob.address,
amount: "10000000000",
});

const signedTx = sealOmnilockTxSkeleton(
txSkeleton,
bob.privKey,
RC_IDENTITY,
smtProofs,
);
const txHash = await rpc.send_transaction(signedTx, "passthrough");
console.log(txHash)
}

main()
Loading

0 comments on commit d3d9125

Please sign in to comment.