Skip to content

Commit

Permalink
feat(config-manager): generate script config from genesis (#552)
Browse files Browse the repository at this point in the history
  • Loading branch information
homura authored Aug 16, 2023
1 parent 54f8aff commit 1cb43fe
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-bags-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ckb-lumos/common-scripts": patch
---

fix incorrect `TX_HASH` when generate a deployment transaction
5 changes: 5 additions & 0 deletions .changeset/two-brooms-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ckb-lumos/config-manager": minor
---

supported generate `ScriptConfig` from a genesis block
2 changes: 1 addition & 1 deletion packages/common-scripts/src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ interface ScriptConfig {

function calculateTxHash(txSkeleton: TransactionSkeletonType): string {
const tx = createTransactionFromSkeleton(txSkeleton);
const txHash = utils.ckbHash(blockchain.Transaction.pack(tx));
const txHash = utils.ckbHash(blockchain.RawTransaction.pack(tx));
return txHash;
}

Expand Down
11 changes: 10 additions & 1 deletion packages/config-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"scripts": {
"fmt": "prettier --write \"{src,tests}/**/*.ts\" package.json",
"lint": "eslint -c ../../.eslintrc.js \"{src,tests}/**/*.ts\"",
"test": "ava --timeout=2m",
"test": "ava **/*.test.{js,ts} --timeout=2m",
"build": "pnpm run build:types && pnpm run build:js",
"build:types": "tsc --declaration --emitDeclarationOnly",
"build:js": "babel --root-mode upward src --out-dir lib --extensions .ts -s",
Expand All @@ -42,5 +42,14 @@
},
"publishConfig": {
"access": "public"
},
"ava": {
"extensions": [
"ts",
"js"
],
"require": [
"ts-node/register"
]
}
}
116 changes: 116 additions & 0 deletions packages/config-manager/src/genesis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Block, Transaction, utils } from "@ckb-lumos/base";
import { ScriptConfig } from "./types";

// https://github.com/nervosnetwork/ckb-sdk-rust/blob/94ce4379454cdaf046f64b346e18e73e029f0ae6/src/constants.rs#L19C1-L24C62
// the index of a transaction in a block
type TransactionIndex = number;
// the index of an output in a transaction
type OutputIndex = number;
export const SIGHASH_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 1];
export const MULTISIG_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 4];
export const DAO_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 2];
export const SIGHASH_GROUP_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [1, 0];
// prettier-ignore
export const MULTISIG_GROUP_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [1, 1];

/**
* Generate {@link ScriptConfig} for the genesis block,
* use this function when you are on a testnet,
* or you cannot determine which network you are on
* @example
* const rpc = new RPC('http://localhost:8114')
* const genesisBlock = await rpc.getBlockByNumber('0x0')
* const scriptConfig = generateGenesisScriptConfigs(genesisBlock)
* @param genesisBlock
*/
export function generateGenesisScriptConfigs(
genesisBlock: Block
): Record<
"SECP256K1_BLAKE160" | "SECP256K1_BLAKE160_MULTISIG" | "DAO",
ScriptConfig
> {
if (!genesisBlock || Number(genesisBlock.header.number) !== 0) {
throw new Error("The block must be a genesis block");
}

const transactions = genesisBlock.transactions;

return {
SECP256K1_BLAKE160: {
...createScriptConfig({
transaction: transactions[SIGHASH_OUTPUT_LOC[0]],
outputIndex: SIGHASH_OUTPUT_LOC[1],
depGroupTransaction: transactions[SIGHASH_GROUP_OUTPUT_LOC[0]],
depGroupOutputIndex: SIGHASH_GROUP_OUTPUT_LOC[1],
}),
SHORT_ID: 0,
},
SECP256K1_BLAKE160_MULTISIG: {
...createScriptConfig({
transaction: transactions[MULTISIG_OUTPUT_LOC[0]],
outputIndex: MULTISIG_OUTPUT_LOC[1],
depGroupTransaction: transactions[MULTISIG_GROUP_OUTPUT_LOC[0]],
depGroupOutputIndex: MULTISIG_GROUP_OUTPUT_LOC[1],
}),
SHORT_ID: 1,
},
DAO: createScriptConfig({
transaction: transactions[DAO_OUTPUT_LOC[0]],
outputIndex: DAO_OUTPUT_LOC[1],
}),
};
}

type ScriptConfigOptions = LocByCode | LocByDepGroup;

type LocByCode = {
transaction: Transaction;
outputIndex: number;
};
type LocByDepGroup = {
transaction: Transaction;
outputIndex: number;
depGroupTransaction: Transaction;
depGroupOutputIndex: number;
};

function createScriptConfig(config: ScriptConfigOptions): ScriptConfig {
const { transaction, outputIndex } = config;

const codeHash = utils.computeScriptHash(
mustGenesisBlock(transaction.outputs[outputIndex]?.type)
);

if ("depGroupTransaction" in config) {
const { depGroupOutputIndex, depGroupTransaction } = config;

return {
HASH_TYPE: "type",
CODE_HASH: codeHash,

DEP_TYPE: "depGroup",
TX_HASH: mustGenesisBlock(depGroupTransaction.hash),
INDEX: toHexNumber(depGroupOutputIndex),
};
}

return {
HASH_TYPE: "type",
CODE_HASH: codeHash,

DEP_TYPE: "code",
INDEX: toHexNumber(outputIndex),
TX_HASH: mustGenesisBlock(transaction.hash),
};
}

function mustGenesisBlock<T>(x: T): NonNullable<T> {
if (x == null) {
throw new Error("The block must be a genesis block");
}
return x;
}

function toHexNumber(number: number): string {
return `0x${number.toString(16)}`;
}
1 change: 1 addition & 0 deletions packages/config-manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./types";
export { initializeConfig, getConfig, validateConfig } from "./manager";
export * as helpers from "./helpers";
export { predefined, createConfig } from "./predefined";
export { generateGenesisScriptConfigs } from "./genesis";
1 change: 1 addition & 0 deletions packages/config-manager/tests/genesis-mainnet-block.json

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions packages/config-manager/tests/genesis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import test from "ava";
import { readFile } from "fs/promises";
import { join } from "path";
import { generateGenesisScriptConfigs, predefined } from "../src";
import { Block } from "@ckb-lumos/base";

test("generateFromGenesisBlock", async (t) => {
const buf = await readFile(join(__dirname, "genesis-mainnet-block.json"));
const genesisBlock = JSON.parse(buf.toString());

const config = generateGenesisScriptConfigs(genesisBlock);

const predefinedConfig = predefined.LINA.SCRIPTS;
t.deepEqual(config, {
SECP256K1_BLAKE160: predefinedConfig.SECP256K1_BLAKE160,
SECP256K1_BLAKE160_MULTISIG: predefinedConfig.SECP256K1_BLAKE160_MULTISIG,
DAO: predefinedConfig.DAO,
});
});

test("generateFromGenesisBlock with wrong block", async (t) => {
const buf = await readFile(join(__dirname, "genesis-mainnet-block.json"));
const genesisBlock: Block = JSON.parse(buf.toString());

t.throws(() => {
const wrongBlock = clone(genesisBlock);
wrongBlock.header.number = "0x111";
generateGenesisScriptConfigs(wrongBlock);
});

t.throws(() => {
const wrongBlock = clone(genesisBlock);
wrongBlock.transactions[0].outputs[1].type = undefined;
generateGenesisScriptConfigs(wrongBlock);
});
});

function clone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}

2 comments on commit 1cb43fe

@vercel
Copy link

@vercel vercel bot commented on 1cb43fe Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 New canary release: 0.0.0-canary-1cb43fe-20230816060512

npm install @ckb-lumos/[email protected]

Please sign in to comment.