Skip to content

Commit 1cb43fe

Browse files
authoredAug 16, 2023
feat(config-manager): generate script config from genesis (#552)
1 parent 54f8aff commit 1cb43fe

File tree

8 files changed

+179
-2
lines changed

8 files changed

+179
-2
lines changed
 

‎.changeset/smart-bags-fetch.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ckb-lumos/common-scripts": patch
3+
---
4+
5+
fix incorrect `TX_HASH` when generate a deployment transaction

‎.changeset/two-brooms-draw.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ckb-lumos/config-manager": minor
3+
---
4+
5+
supported generate `ScriptConfig` from a genesis block

‎packages/common-scripts/src/deploy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ interface ScriptConfig {
361361

362362
function calculateTxHash(txSkeleton: TransactionSkeletonType): string {
363363
const tx = createTransactionFromSkeleton(txSkeleton);
364-
const txHash = utils.ckbHash(blockchain.Transaction.pack(tx));
364+
const txHash = utils.ckbHash(blockchain.RawTransaction.pack(tx));
365365
return txHash;
366366
}
367367

‎packages/config-manager/package.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"scripts": {
2525
"fmt": "prettier --write \"{src,tests}/**/*.ts\" package.json",
2626
"lint": "eslint -c ../../.eslintrc.js \"{src,tests}/**/*.ts\"",
27-
"test": "ava --timeout=2m",
27+
"test": "ava **/*.test.{js,ts} --timeout=2m",
2828
"build": "pnpm run build:types && pnpm run build:js",
2929
"build:types": "tsc --declaration --emitDeclarationOnly",
3030
"build:js": "babel --root-mode upward src --out-dir lib --extensions .ts -s",
@@ -42,5 +42,14 @@
4242
},
4343
"publishConfig": {
4444
"access": "public"
45+
},
46+
"ava": {
47+
"extensions": [
48+
"ts",
49+
"js"
50+
],
51+
"require": [
52+
"ts-node/register"
53+
]
4554
}
4655
}
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Block, Transaction, utils } from "@ckb-lumos/base";
2+
import { ScriptConfig } from "./types";
3+
4+
// https://github.com/nervosnetwork/ckb-sdk-rust/blob/94ce4379454cdaf046f64b346e18e73e029f0ae6/src/constants.rs#L19C1-L24C62
5+
// the index of a transaction in a block
6+
type TransactionIndex = number;
7+
// the index of an output in a transaction
8+
type OutputIndex = number;
9+
export const SIGHASH_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 1];
10+
export const MULTISIG_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 4];
11+
export const DAO_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [0, 2];
12+
export const SIGHASH_GROUP_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [1, 0];
13+
// prettier-ignore
14+
export const MULTISIG_GROUP_OUTPUT_LOC: [TransactionIndex, OutputIndex] = [1, 1];
15+
16+
/**
17+
* Generate {@link ScriptConfig} for the genesis block,
18+
* use this function when you are on a testnet,
19+
* or you cannot determine which network you are on
20+
* @example
21+
* const rpc = new RPC('http://localhost:8114')
22+
* const genesisBlock = await rpc.getBlockByNumber('0x0')
23+
* const scriptConfig = generateGenesisScriptConfigs(genesisBlock)
24+
* @param genesisBlock
25+
*/
26+
export function generateGenesisScriptConfigs(
27+
genesisBlock: Block
28+
): Record<
29+
"SECP256K1_BLAKE160" | "SECP256K1_BLAKE160_MULTISIG" | "DAO",
30+
ScriptConfig
31+
> {
32+
if (!genesisBlock || Number(genesisBlock.header.number) !== 0) {
33+
throw new Error("The block must be a genesis block");
34+
}
35+
36+
const transactions = genesisBlock.transactions;
37+
38+
return {
39+
SECP256K1_BLAKE160: {
40+
...createScriptConfig({
41+
transaction: transactions[SIGHASH_OUTPUT_LOC[0]],
42+
outputIndex: SIGHASH_OUTPUT_LOC[1],
43+
depGroupTransaction: transactions[SIGHASH_GROUP_OUTPUT_LOC[0]],
44+
depGroupOutputIndex: SIGHASH_GROUP_OUTPUT_LOC[1],
45+
}),
46+
SHORT_ID: 0,
47+
},
48+
SECP256K1_BLAKE160_MULTISIG: {
49+
...createScriptConfig({
50+
transaction: transactions[MULTISIG_OUTPUT_LOC[0]],
51+
outputIndex: MULTISIG_OUTPUT_LOC[1],
52+
depGroupTransaction: transactions[MULTISIG_GROUP_OUTPUT_LOC[0]],
53+
depGroupOutputIndex: MULTISIG_GROUP_OUTPUT_LOC[1],
54+
}),
55+
SHORT_ID: 1,
56+
},
57+
DAO: createScriptConfig({
58+
transaction: transactions[DAO_OUTPUT_LOC[0]],
59+
outputIndex: DAO_OUTPUT_LOC[1],
60+
}),
61+
};
62+
}
63+
64+
type ScriptConfigOptions = LocByCode | LocByDepGroup;
65+
66+
type LocByCode = {
67+
transaction: Transaction;
68+
outputIndex: number;
69+
};
70+
type LocByDepGroup = {
71+
transaction: Transaction;
72+
outputIndex: number;
73+
depGroupTransaction: Transaction;
74+
depGroupOutputIndex: number;
75+
};
76+
77+
function createScriptConfig(config: ScriptConfigOptions): ScriptConfig {
78+
const { transaction, outputIndex } = config;
79+
80+
const codeHash = utils.computeScriptHash(
81+
mustGenesisBlock(transaction.outputs[outputIndex]?.type)
82+
);
83+
84+
if ("depGroupTransaction" in config) {
85+
const { depGroupOutputIndex, depGroupTransaction } = config;
86+
87+
return {
88+
HASH_TYPE: "type",
89+
CODE_HASH: codeHash,
90+
91+
DEP_TYPE: "depGroup",
92+
TX_HASH: mustGenesisBlock(depGroupTransaction.hash),
93+
INDEX: toHexNumber(depGroupOutputIndex),
94+
};
95+
}
96+
97+
return {
98+
HASH_TYPE: "type",
99+
CODE_HASH: codeHash,
100+
101+
DEP_TYPE: "code",
102+
INDEX: toHexNumber(outputIndex),
103+
TX_HASH: mustGenesisBlock(transaction.hash),
104+
};
105+
}
106+
107+
function mustGenesisBlock<T>(x: T): NonNullable<T> {
108+
if (x == null) {
109+
throw new Error("The block must be a genesis block");
110+
}
111+
return x;
112+
}
113+
114+
function toHexNumber(number: number): string {
115+
return `0x${number.toString(16)}`;
116+
}

‎packages/config-manager/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from "./types";
22
export { initializeConfig, getConfig, validateConfig } from "./manager";
33
export * as helpers from "./helpers";
44
export { predefined, createConfig } from "./predefined";
5+
export { generateGenesisScriptConfigs } from "./genesis";

‎packages/config-manager/tests/genesis-mainnet-block.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import test from "ava";
2+
import { readFile } from "fs/promises";
3+
import { join } from "path";
4+
import { generateGenesisScriptConfigs, predefined } from "../src";
5+
import { Block } from "@ckb-lumos/base";
6+
7+
test("generateFromGenesisBlock", async (t) => {
8+
const buf = await readFile(join(__dirname, "genesis-mainnet-block.json"));
9+
const genesisBlock = JSON.parse(buf.toString());
10+
11+
const config = generateGenesisScriptConfigs(genesisBlock);
12+
13+
const predefinedConfig = predefined.LINA.SCRIPTS;
14+
t.deepEqual(config, {
15+
SECP256K1_BLAKE160: predefinedConfig.SECP256K1_BLAKE160,
16+
SECP256K1_BLAKE160_MULTISIG: predefinedConfig.SECP256K1_BLAKE160_MULTISIG,
17+
DAO: predefinedConfig.DAO,
18+
});
19+
});
20+
21+
test("generateFromGenesisBlock with wrong block", async (t) => {
22+
const buf = await readFile(join(__dirname, "genesis-mainnet-block.json"));
23+
const genesisBlock: Block = JSON.parse(buf.toString());
24+
25+
t.throws(() => {
26+
const wrongBlock = clone(genesisBlock);
27+
wrongBlock.header.number = "0x111";
28+
generateGenesisScriptConfigs(wrongBlock);
29+
});
30+
31+
t.throws(() => {
32+
const wrongBlock = clone(genesisBlock);
33+
wrongBlock.transactions[0].outputs[1].type = undefined;
34+
generateGenesisScriptConfigs(wrongBlock);
35+
});
36+
});
37+
38+
function clone<T>(obj: T): T {
39+
return JSON.parse(JSON.stringify(obj));
40+
}

2 commit comments

Comments
 (2)

vercel[bot] commented on Aug 16, 2023

@vercel[bot]

github-actions[bot] commented on Aug 16, 2023

@github-actions[bot]
Contributor

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

npm install @ckb-lumos/lumos@0.0.0-canary-1cb43fe-20230816060512
Please sign in to comment.