diff --git a/.changeset/old-houses-smell.md b/.changeset/old-houses-smell.md new file mode 100644 index 000000000..3d18193ae --- /dev/null +++ b/.changeset/old-houses-smell.md @@ -0,0 +1,5 @@ +--- +"@ckb-lumos/rpc": minor +--- + +`unknown` and `rejected` are available in the `TransactionWithStatus` diff --git a/.changeset/small-trees-appear.md b/.changeset/small-trees-appear.md new file mode 100644 index 000000000..4a70f3910 --- /dev/null +++ b/.changeset/small-trees-appear.md @@ -0,0 +1,5 @@ +--- +"@ckb-lumos/rpc": minor +--- + +Supporting CKB2023, please check out [nervosnetwork/ckb#3980](https://github.com/nervosnetwork/ckb/pull/3980) and [nervosnetwork/ckb#3963](https://github.com/nervosnetwork/ckb/pull/3963) for more details diff --git a/.changeset/ten-keys-argue.md b/.changeset/ten-keys-argue.md new file mode 100644 index 000000000..f0cd3f57f --- /dev/null +++ b/.changeset/ten-keys-argue.md @@ -0,0 +1,5 @@ +--- +"@ckb-lumos/rpc": minor +--- + +Supporting different return values when passing in different verbosity on `getBlock`, `getBlockByNumber`, `getHeader` and `getHeaderByNumber` with typescript diff --git a/.github/workflows/canary-ckb2023.yml b/.github/workflows/canary-ckb2023.yml new file mode 100644 index 000000000..de55c42a8 --- /dev/null +++ b/.github/workflows/canary-ckb2023.yml @@ -0,0 +1,48 @@ +name: Canary-CKB2023 +on: + push: + branches: + - ckb2023 + +permissions: + contents: write + +jobs: + canary: + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v3 + + - name: Install dependencies + uses: ./.github/actions/install-deps + + - name: Setup .npmrc file + uses: actions/setup-node@v3 + with: + registry-url: "https://registry.npmjs.org" + + - name: Canary release + run: | + npx changeset pre exit || true + npx changeset version --snapshot ckb2023-$(git log -1 --pretty=format:%h) + pnpm -r publish --no-git-checks --tag ckb2023 + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate commit comment + id: commit-comment + run: | + result="$(node scripts/canary-commit-comment.cjs)" + delimiter="$(openssl rand -hex 8)" + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo "$result" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + + - name: Create commit comment + uses: peter-evans/commit-comment@v2 + with: + body: ${{ steps.commit-comment.outputs.result }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/commitlint.config.js b/commitlint.config.js index 8597e53f1..d3e6b8928 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -8,6 +8,7 @@ const scopeEnumValues = [ "hd-cache", "helpers", "indexer", + "light-client", "lumos", "rpc", "sql-indexer", diff --git a/examples/cardano-lock-namiwallet/package.json b/examples/cardano-lock-namiwallet/package.json index 438f90e6b..4a651281c 100644 --- a/examples/cardano-lock-namiwallet/package.json +++ b/examples/cardano-lock-namiwallet/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@lumos-examples/cardano-lock-namiwallet", - "version": "0.20.0", + "version": "0.21.0-next.0", "description": "", "main": "index.js", "scripts": { diff --git a/examples/omni-lock-metamask/package.json b/examples/omni-lock-metamask/package.json index 26d25396b..b9ac1a892 100644 --- a/examples/omni-lock-metamask/package.json +++ b/examples/omni-lock-metamask/package.json @@ -17,18 +17,5 @@ "@types/react-dom": "^18.0.9", "react": "18.2.0", "react-dom": "18.2.0" - }, - "devDependencies": { - "parcel": "^2.9.3", - "buffer": "^5.5.0", - "crypto-browserify": "^3.12.0", - "events": "^3.1.0", - "path-browserify": "^1.0.0", - "process": "^0.11.10", - "stream-browserify": "^3.0.0", - "string_decoder": "^1.3.0" - }, - "alias": { - "process": false } } diff --git a/examples/pnpm-lock.yaml b/examples/pnpm-lock.yaml index ab8458098..6503420a3 100644 --- a/examples/pnpm-lock.yaml +++ b/examples/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -39,13 +39,13 @@ importers: ../packages/base: dependencies: '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit '@types/blake2b': specifier: ^2.1.0 @@ -76,19 +76,19 @@ importers: ../packages/ckb-indexer: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit cross-fetch: specifier: ^3.1.5 @@ -98,7 +98,7 @@ importers: version: 3.3.0 devDependencies: '@ckb-lumos/testkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../testkit '@types/lodash.uniqby': specifier: ^4.7.7 @@ -125,7 +125,7 @@ importers: ../packages/codec: dependencies: '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi devDependencies: escape-string-regexp: @@ -138,35 +138,35 @@ importers: ../packages/common-scripts: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/helpers': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../helpers '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit immutable: specifier: ^4.3.0 version: 4.3.0 devDependencies: '@ckb-lumos/debugger': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../debugger '@ckb-lumos/hd': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../hd '@types/keccak': specifier: ^3.0.1 @@ -178,13 +178,13 @@ importers: ../packages/config-manager: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@types/deep-freeze-strict': specifier: ^1.1.0 @@ -196,22 +196,22 @@ importers: ../packages/debugger: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/helpers': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../helpers '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@types/download': specifier: ^8.0.1 @@ -233,55 +233,55 @@ importers: version: 3.0.0 devDependencies: '@ckb-lumos/common-scripts': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../common-scripts '@ckb-lumos/experiment-tx-assembler': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../experiment-tx-assembler '@ckb-lumos/hd': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../hd ../packages/e2e-test: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/ckb-indexer': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../ckb-indexer '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/common-scripts': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../common-scripts '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/hd': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../hd '@ckb-lumos/helpers': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../helpers '@ckb-lumos/light-client': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../light-client '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@ckb-lumos/runner': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../runner '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit '@ckb-lumos/utils': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../utils '@types/kill-port': specifier: ^2.0.0 @@ -294,7 +294,7 @@ importers: version: 2.0.1 devDependencies: '@ckb-lumos/testkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../testkit '@types/request': specifier: ^2.48.8 @@ -315,28 +315,28 @@ importers: ../packages/experiment-tx-assembler: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/helpers': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../helpers '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit ../packages/hd: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi bn.js: specifier: ^5.1.3 @@ -367,22 +367,22 @@ importers: ../packages/hd-cache: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/ckb-indexer': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../ckb-indexer '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/hd': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../hd '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc immutable: specifier: ^4.3.0 @@ -398,19 +398,19 @@ importers: ../packages/helpers: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit bech32: specifier: ^2.0.0 @@ -422,13 +422,13 @@ importers: ../packages/light-client: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/ckb-indexer': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../ckb-indexer '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc cross-fetch: specifier: ^3.1.5 @@ -438,7 +438,7 @@ importers: version: 3.3.0 devDependencies: '@ckb-lumos/testkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../testkit '@types/request': specifier: ^2.48.8 @@ -453,31 +453,31 @@ importers: ../packages/lumos: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/ckb-indexer': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../ckb-indexer '@ckb-lumos/common-scripts': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../common-scripts '@ckb-lumos/config-manager': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../config-manager '@ckb-lumos/hd': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../hd '@ckb-lumos/helpers': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../helpers '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit devDependencies: buffer: @@ -505,10 +505,10 @@ importers: ../packages/molecule: dependencies: '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@types/nearley': specifier: ^2.11.2 @@ -521,7 +521,7 @@ importers: version: 2.20.1 devDependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base jsbi: specifier: ^4.1.0 @@ -530,10 +530,10 @@ importers: ../packages/rpc: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@vespaiach/axios-fetch-adapter': specifier: ^0.3.1 @@ -564,7 +564,7 @@ importers: ../packages/runner: dependencies: '@ckb-lumos/utils': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../utils '@ltd/j-toml': specifier: ^1.38.0 @@ -600,14 +600,17 @@ importers: ../packages/testkit: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/bi': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../bi '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec + '@ckb-lumos/rpc': + specifier: 0.21.0-next.0 + version: link:../rpc '@types/body-parser': specifier: ^1.19.1 version: 1.19.1 @@ -659,19 +662,19 @@ importers: ../packages/transaction-manager: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../base '@ckb-lumos/ckb-indexer': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../ckb-indexer '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../codec '@ckb-lumos/rpc': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../rpc '@ckb-lumos/toolkit': - specifier: 0.20.0 + specifier: 0.21.0-next.0 version: link:../toolkit immutable: specifier: ^4.3.0 @@ -794,31 +797,6 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) - devDependencies: - buffer: - specifier: ^5.5.0 - version: 5.5.0 - crypto-browserify: - specifier: ^3.12.0 - version: 3.12.0 - events: - specifier: ^3.1.0 - version: 3.3.0 - parcel: - specifier: ^2.9.3 - version: 2.9.3 - path-browserify: - specifier: ^1.0.0 - version: 1.0.0 - process: - specifier: ^0.11.10 - version: 0.11.10 - stream-browserify: - specifier: ^3.0.0 - version: 3.0.0 - string_decoder: - specifier: ^1.3.0 - version: 1.3.0 omni-lock-secp256k1-blake160: dependencies: @@ -906,13 +884,13 @@ importers: secp256k1-transfer: dependencies: '@ckb-lumos/base': - specifier: 0.20.0 + specifier: canary version: link:../../packages/base '@ckb-lumos/codec': - specifier: 0.20.0 + specifier: canary version: link:../../packages/codec '@ckb-lumos/lumos': - specifier: 0.20.0 + specifier: canary version: link:../../packages/lumos '@types/react': specifier: ^18.0.25 @@ -6864,6 +6842,7 @@ packages: /node-gyp-build-optional-packages@5.0.7: resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} hasBin: true + requiresBuild: true dev: true optional: true diff --git a/examples/pw-lock-metamask/package.json b/examples/pw-lock-metamask/package.json index 2ee2b342f..7be630846 100644 --- a/examples/pw-lock-metamask/package.json +++ b/examples/pw-lock-metamask/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@lumos-examples/pw-lock-metamask", - "version": "0.20.0", + "version": "0.21.0-next.0", "description": "", "main": "index.js", "scripts": { diff --git a/examples/secp256k1-multisig-transfer/package.json b/examples/secp256k1-multisig-transfer/package.json index f567ad376..280c2bdf0 100644 --- a/examples/secp256k1-multisig-transfer/package.json +++ b/examples/secp256k1-multisig-transfer/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@lumos-examples/secp256k1-multisig-transfer", - "version": "0.20.0", + "version": "0.21.0-next.0", "description": "", "main": "index.js", "scripts": { diff --git a/examples/secp256k1-transfer/lib.ts b/examples/secp256k1-transfer/lib.ts index 2fa6ee107..691ec9fd5 100644 --- a/examples/secp256k1-transfer/lib.ts +++ b/examples/secp256k1-transfer/lib.ts @@ -3,8 +3,9 @@ import { Indexer, helpers, Address, Script, RPC, hd, config, Cell, commons, Witn import { values, blockchain } from "@ckb-lumos/base"; const { ScriptValue } = values; -export const { AGGRON4 } = config.predefined; - +// export let { AGGRON4 } = config.predefined; +let AGGRON4: config.Config = config.predefined.AGGRON4; +// it can be changed to the devnet http://localhost:8114 const CKB_RPC_URL = "https://testnet.ckb.dev/rpc"; const rpc = new RPC(CKB_RPC_URL); const indexer = new Indexer(CKB_RPC_URL); @@ -52,6 +53,9 @@ interface Options { } export async function transfer(options: Options): Promise { + const block = await rpc.getBlockByNumber("0x0"); + AGGRON4 = { PREFIX: "ckt", SCRIPTS: config.generateGenesisScriptConfigs(block) }; + let txSkeleton = helpers.TransactionSkeleton({}); const fromScript = helpers.parseAddress(options.from, { config: AGGRON4 }); const toScript = helpers.parseAddress(options.to, { config: AGGRON4 }); diff --git a/examples/secp256k1-transfer/package.json b/examples/secp256k1-transfer/package.json index 203090c4b..219723f92 100644 --- a/examples/secp256k1-transfer/package.json +++ b/examples/secp256k1-transfer/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@lumos-examples/secp256k1-transfer", - "version": "0.20.0", + "version": "0.21.0-next.0", "description": "", "main": "index.js", "scripts": { @@ -12,9 +12,9 @@ "author": "", "license": "MIT", "dependencies": { - "@ckb-lumos/base": "0.20.0", - "@ckb-lumos/codec": "0.20.0", - "@ckb-lumos/lumos": "0.20.0", + "@ckb-lumos/base": "canary", + "@ckb-lumos/codec": "canary", + "@ckb-lumos/lumos": "canary", "@types/react": "^18.0.25", "@types/react-dom": "^18.0.9", "react": "^18.2.0", diff --git a/lerna.json b/lerna.json index bd1eb6807..156ae74da 100644 --- a/lerna.json +++ b/lerna.json @@ -5,7 +5,7 @@ ], "npmClient": "pnpm", "useWorkspaces": true, - "version": "0.20.0", + "version": "0.21.0-next.0", "command": { "publish": { "access": "public" diff --git a/packages/base/src/api.ts b/packages/base/src/api.ts index b7d1865a6..94cf2dc6b 100644 --- a/packages/base/src/api.ts +++ b/packages/base/src/api.ts @@ -1,4 +1,20 @@ -import { Hash, HexNumber, HexString, PackedSince } from "./primitive"; +// TODO: this file is a copy of jsonrpc-types/src/blockchain.rs +// it provides some basic types for RPC, also for Lumos, +// but it is not a good idea to use these types directly, +// RPC types are not stable, Lumos is better to define its own types +// instead of using the RPC types in the future, the api.ts is a legacy solution. +// To map the RPC types, it is better to 1:1 map the `jsonrpc-types` from the ckb repo, +// at https://github.com/nervosnetwork/ckb/tree/develop/util/jsonrpc-types/src +// with the same directory structure. + +import { + Hash, + Hexadecimal, + HexNumber, + HexString, + PackedSince, +} from "./primitive"; + export interface Header { timestamp: HexNumber; number: HexNumber; @@ -14,7 +30,7 @@ export interface Header { version: HexNumber; } -export type HashType = "type" | "data" | "data1"; +export type HashType = "type" | "data" | "data1" | "data2"; export interface Script { codeHash: Hash; hashType: HashType; @@ -68,14 +84,19 @@ export interface Transaction { witnesses: HexString[]; } +type Status = "pending" | "proposed" | "committed" | "unknown" | "rejected"; + export interface TxStatus { + status: Status; blockHash?: Hash; - status: string; + reason?: string; } -export interface TransactionWithStatus { - transaction: Transaction; +export interface TransactionWithStatus { + transaction: Tx; txStatus: TxStatus; + timeAddedToPool: Uint64 | null; + cycles: Uint64 | null; } export interface Cell { @@ -161,8 +182,6 @@ export interface ProposalWindow { export interface Consensus { id: string; genesisHash: Hash; - // added this field by: https://github.com/nervosnetwork/ckb/pull/2879 - hardforkFeatures: Array<{ rfc: string; epochNumber: string | undefined }>; daoTypeHash?: Hash; secp256k1Blake160SighashAllTypeHash?: Hash; secp256k1Blake160MultisigAllTypeHash?: Hash; @@ -183,8 +202,47 @@ export interface Consensus { maxBlockProposalsLimit: HexNumber; primaryEpochRewardHalvingInterval: HexNumber; permanentDifficultyInDummy: boolean; + // added this field by: https://github.com/nervosnetwork/ckb/pull/2879 + hardforkFeatures: HardForks; + softforks: MapLike; } +export type HardForks = HardforkFeature[]; + +export interface HardforkFeature { + rfc: string; + epochNumber: EpochNumber | null; +} + +export type SoftForkStatus = "buried" | "rfc0043"; + +export type SoftFork = Buried | Rfc0043; + +export type Buried = { + status: SoftForkStatus; + active: boolean; + epoch: EpochNumber; +}; + +export type Rfc0043 = { + status: SoftForkStatus; + rfc0043: Deployment; +}; + +export type Ratio = { + numer: Uint64; + denom: Uint64; +}; + +export type Deployment = { + bit: number; + start: EpochNumber; + timeout: EpochNumber; + minActivationEpoch: EpochNumber; + period: EpochNumber; + threshold: Ratio; +}; + export interface DryRunResult { cycles: HexNumber; } @@ -290,6 +348,8 @@ export interface TxPoolVerbosity { export type RawTxPool = TxPoolIds | TxPoolVerbosity; +/** https://github.com/nervosnetwork/ckb/blob/develop/util/jsonrpc-types/src/alert.rs **/ + export interface AlertMessage { id: HexNumber; priority: HexNumber; @@ -297,15 +357,6 @@ export interface AlertMessage { message: string; } -export interface ChainInfo { - chain: string; - medianTime: HexNumber; - epoch: HexNumber; - difficulty: HexNumber; - isInitialBlockDownload: boolean; - alerts: AlertMessage[]; -} - export interface Alert { id: HexNumber; cancel: HexNumber; @@ -316,3 +367,48 @@ export interface Alert { message: string; signatures: HexString[]; } + +/** https://github.com/nervosnetwork/ckb/blob/develop/util/jsonrpc-types/src/info.rs **/ + +export type DeploymentPos = "testdummy" | "lightClient"; +export type DeploymentState = + | "defined" + | "started" + | "lockedIn" + | "active" + | "failed"; +export type DeploymentsInfo = { + hash: Hash; + epoch: EpochNumber; + deployments: MapLike; +}; +export type DeploymentInfo = { + // determines which bit in the version field of the block is to be used to signal the softfork lock-in and activation. It is chosen from the set {0,1,2,…,28}. + bit: number; + start: EpochNumber; + timeout: EpochNumber; + minActivationEpoch: EpochNumber; + period: EpochNumber; + threshold: Ratio; + since: EpochNumber; + state: DeploymentState; +}; + +export interface ChainInfo { + chain: string; + medianTime: HexNumber; + epoch: EpochNumberWithFraction; + difficulty: HexNumber; + isInitialBlockDownload: boolean; + alerts: AlertMessage[]; +} + +type Uint64 = Hexadecimal; +type EpochNumber = Hexadecimal; +type EpochNumberWithFraction = Uint64; + +// this is a type to mapping the `HashMap`, `BTreeMap` in `jsonrpc-types` +// there are some returns of CKB RPC are in this format, like `Softfork` +type MapLike = { + [key in K]?: V; +}; diff --git a/packages/base/src/blockchain.ts b/packages/base/src/blockchain.ts index e72e0eb20..5f2b90a4f 100644 --- a/packages/base/src/blockchain.ts +++ b/packages/base/src/blockchain.ts @@ -103,22 +103,36 @@ export const WitnessArgs = WitnessArgsOf({ }); /** + *
+ *  0b0000000 0
+ *    ───┬─── │
+ *       │    ▼
+ *       │   type - use the default vm version
+ *       │
+ *       ▼
+ * data* - use a particular vm version
+ * 
+ * * Implementation of blockchain.mol * https://github.com/nervosnetwork/ckb/blob/5a7efe7a0b720de79ff3761dc6e8424b8d5b22ea/util/types/schemas/blockchain.mol */ export const HashType = createFixedBytesCodec({ byteLength: 1, pack: (type) => { - if (type === "data") return Uint8.pack(0); - if (type === "type") return Uint8.pack(1); - if (type === "data1") return Uint8.pack(2); + // prettier-ignore + if (type === "type") return Uint8.pack(0b0000000_1); + // prettier-ignore + if (type === "data") return Uint8.pack(0b0000000_0); + if (type === "data1") return Uint8.pack(0b0000001_0); + if (type === "data2") return Uint8.pack(0b0000010_0); throw new Error(`Invalid hash type: ${type}`); }, unpack: (buf) => { const hashTypeBuf = Uint8.unpack(buf); - if (hashTypeBuf === 0) return "data"; - if (hashTypeBuf === 1) return "type"; - if (hashTypeBuf === 2) return "data1"; + if (hashTypeBuf === 0b0000000_1) return "type"; + if (hashTypeBuf === 0b0000000_0) return "data"; + if (hashTypeBuf === 0b0000001_0) return "data1"; + if (hashTypeBuf === 0b0000010_0) return "data2"; throw new Error(`Invalid hash type: ${hashTypeBuf}`); }, }); diff --git a/packages/base/tests/serialize.test.ts b/packages/base/tests/serialize.test.ts index f40b0d049..b3e78a2d0 100644 --- a/packages/base/tests/serialize.test.ts +++ b/packages/base/tests/serialize.test.ts @@ -31,6 +31,20 @@ test("serialize ckb2021 script", (t) => { ); }); +test("serialize ckb2023 script", (t) => { + const value: api.Script = { + codeHash: + "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + args: "0xaabbccdd44332211", + hashType: "data2", + }; + const serializedHex = bytes.hexify(blockchain.Script.pack(value)); + t.deepEqual( + serializedHex, + "0x3d0000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80408000000aabbccdd44332211" + ); +}); + test("serialize outpoint", (t) => { const value: api.OutPoint = { txHash: diff --git a/packages/ckb-indexer/src/rpcType.ts b/packages/ckb-indexer/src/rpcType.ts index dda3b0155..4b31646df 100644 --- a/packages/ckb-indexer/src/rpcType.ts +++ b/packages/ckb-indexer/src/rpcType.ts @@ -1,4 +1,10 @@ -import { HexString, HexNumber, Hash, Hexadecimal } from "@ckb-lumos/base"; +import { + HexString, + HexNumber, + Hash, + Hexadecimal, + HashType, +} from "@ckb-lumos/base"; export type Tip = { block_hash: HexNumber; @@ -6,7 +12,7 @@ export type Tip = { }; export type Script = { code_hash: HexString; - hash_type: "type" | "data" | "data1"; + hash_type: HashType; args: HexString; }; export interface OutPoint { diff --git a/packages/ckb-indexer/src/transaction_collector.ts b/packages/ckb-indexer/src/transaction_collector.ts index e8cb9935e..4515590a9 100644 --- a/packages/ckb-indexer/src/transaction_collector.ts +++ b/packages/ckb-indexer/src/transaction_collector.ts @@ -173,10 +173,14 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio return this.isCellScriptArgsValid(resolvedCell); } ); - const objects = filteredTransactionList.map((tx) => ({ - transaction: tx.transaction, - txStatus: tx.txStatus, - })); + const objects = filteredTransactionList.map( + (tx) => ({ + transaction: tx.transaction, + txStatus: tx.txStatus, + cycles: tx.cycles, + timeAddedToPool: tx.timeAddedToPool, + }) + ); return objects; } diff --git a/packages/ckb-indexer/tests/test_cases.js b/packages/ckb-indexer/tests/test_cases.js index 893447798..d914b1ca1 100644 --- a/packages/ckb-indexer/tests/test_cases.js +++ b/packages/ckb-indexer/tests/test_cases.js @@ -7079,8 +7079,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7115,8 +7117,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x7bb56e1288a1de98bab23d3e0ec7728634b6626ab03cc119ec23005a82ff12ff', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7151,8 +7155,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x21d6e5b949392186a6510c57da615f086f779b713a7f3d54c82a07d443e85c5d', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7187,8 +7193,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x3ea3a5122344ceb335ff776dbfa5f3e5a06b6edd965414f287f0d57f92304c89', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7223,8 +7231,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x1255b3b013addc93fb0301ad2a4150d15e6c1a1c4badbc653fc821b127a37929', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7259,8 +7269,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x2c96b715a2e2fe5602d518d75bc4912e6be16f34d9f1f2f420ff6e6de40c9379', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7295,8 +7307,10 @@ const transactionByLock = [ }, txStatus: { blockHash: '0x02bfadbf60172d77af71ad141536d14f5ba191b40b5cffb2b2e7905459e8d500', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, ] @@ -7381,8 +7395,10 @@ const transactionsByType = [ }, txStatus: { blockHash: '0x4cc7b42c12e0ed1c87c3ced726e419ba19f755be1739097d6758b6bf60c654ad', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null }, { transaction: { @@ -7445,8 +7461,10 @@ const transactionsByType = [ }, txStatus: { blockHash: '0xd4b10e5af3dac133888f47baeda057f7760fb4f81b2f4dc03a29c228c7dba7a0', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null } ]; @@ -7512,8 +7530,10 @@ const transactionsByLockAndType = [ }, txStatus: { blockHash: '0xd4b10e5af3dac133888f47baeda057f7760fb4f81b2f4dc03a29c228c7dba7a0', - status: 'pending', - } + status: 'committed', + }, + cycles: null, + timeAddedToPool: null } ]; diff --git a/packages/ckb-indexer/tests/transaction_collector.unit.test.ts b/packages/ckb-indexer/tests/transaction_collector.unit.test.ts index 347234a31..300eafdaf 100644 --- a/packages/ckb-indexer/tests/transaction_collector.unit.test.ts +++ b/packages/ckb-indexer/tests/transaction_collector.unit.test.ts @@ -308,5 +308,7 @@ function mockTxWithStatus({ cellDeps: [], }, txStatus: { status: "committed" }, + timeAddedToPool: null, + cycles: null, }; } diff --git a/packages/ckb-indexer/tests/transaction_collector_unit_test_case.ts b/packages/ckb-indexer/tests/transaction_collector_unit_test_case.ts index 75992d562..dbcac57f2 100644 --- a/packages/ckb-indexer/tests/transaction_collector_unit_test_case.ts +++ b/packages/ckb-indexer/tests/transaction_collector_unit_test_case.ts @@ -164,6 +164,8 @@ export const unresolvedTransaction: TransactionWithStatus = { "0xbbb1f70add4698b3b9f2ee1d475a1c391c4337c135680169269ca61afacd00fc", status: "committed", }, + cycles: null, + timeAddedToPool: null, }; export const unresolvedTransactionList: TransactionWithStatus[] = [ unresolvedTransaction, @@ -249,6 +251,8 @@ export const resolvedTransaction: TransactionWithStatus = { "0x4b3422f42a72c313c74d4b1c15a89fa16d466f8602dea9cd097cf0544177356d", status: "committed", }, + cycles: null, + timeAddedToPool: null, }; export const batchRequestTransaction = [ diff --git a/packages/common-scripts/src/deploy.ts b/packages/common-scripts/src/deploy.ts index c71e64875..8133113f9 100644 --- a/packages/common-scripts/src/deploy.ts +++ b/packages/common-scripts/src/deploy.ts @@ -346,7 +346,7 @@ interface ScriptConfig { // if hashType is data, codeHash is ckbHash(data) CODE_HASH: string; - HASH_TYPE: "type" | "data1"; + HASH_TYPE: "type" | "data2"; TX_HASH: string; // the deploy cell can be found at index of tx's outputs @@ -374,7 +374,7 @@ function getScriptConfigByDataHash( const txHash = calculateTxHash(txSkeleton); const scriptConfig: ScriptConfig = { CODE_HASH: codeHash, - HASH_TYPE: "data1", + HASH_TYPE: "data2", TX_HASH: txHash, INDEX: "0x0", DEP_TYPE: "code", diff --git a/packages/config-manager/src/manager.ts b/packages/config-manager/src/manager.ts index 80ef87e06..98869cc6a 100644 --- a/packages/config-manager/src/manager.ts +++ b/packages/config-manager/src/manager.ts @@ -25,6 +25,17 @@ function assertInteger(debugPath: string, i: string) { } } +export function assertHashType(debugPath: string, hashType: string): void { + if ( + hashType !== "type" && + hashType !== "data" && + hashType !== "data1" && + hashType !== "data2" + ) { + throw new Error(`${debugPath} must one of type, data, data1, data2!`); + } +} + function assert(condition: unknown, debugPath = "variable"): asserts condition { if (!condition) throw new Error(`${debugPath} is not valid`); } @@ -41,12 +52,7 @@ export function validateConfig(config: Config): void { assert(scriptConfig?.CODE_HASH); assertHash(`SCRIPTS.${scriptName}.CODE_HASH`, scriptConfig.CODE_HASH); - const hashType = scriptConfig.HASH_TYPE; - if (hashType !== "type" && hashType !== "data" && hashType !== "data1") { - throw new Error( - `SCRIPTS.${scriptName}.HASH_TYPE must be type, data or data1!` - ); - } + assertHashType(`SCRIPTS.${scriptName}.HASH_TYPE`, scriptConfig.HASH_TYPE); assertHash(`SCRIPTS.${scriptName}.TX_HASH`, scriptConfig.TX_HASH); assertInteger(`SCRIPTS.${scriptName}.INDEX`, scriptConfig.INDEX); const depType = scriptConfig.DEP_TYPE; diff --git a/packages/config-manager/src/types.ts b/packages/config-manager/src/types.ts index d669ff8be..c0f89d84d 100644 --- a/packages/config-manager/src/types.ts +++ b/packages/config-manager/src/types.ts @@ -1,7 +1,9 @@ +import { HashType } from "@ckb-lumos/base"; + /** Deployed script on chain */ export interface ScriptConfig { CODE_HASH: string; - HASH_TYPE: "type" | "data" | "data1"; + HASH_TYPE: HashType; TX_HASH: string; INDEX: string; DEP_TYPE: "depGroup" | "code"; diff --git a/packages/config-manager/tests/validator.js b/packages/config-manager/tests/validator.test.ts similarity index 51% rename from packages/config-manager/tests/validator.js rename to packages/config-manager/tests/validator.test.ts index f9f6c2edf..d5c0623c5 100644 --- a/packages/config-manager/tests/validator.js +++ b/packages/config-manager/tests/validator.test.ts @@ -1,15 +1,16 @@ -const test = require("ava"); - -const { validateConfig, predefined } = require("../lib/index"); +import test from "ava"; +import { validateConfig, predefined } from "../src"; +import { assertHashType } from "../src/manager"; test.before(() => { - BigInt = () => { + BigInt = (() => { throw new Error("can not find bigint"); - }; + }) as unknown as BigIntConstructor; }); test("validate all predefined config", (t) => { - for (const name of Object.keys(predefined)) { + const keys = Object.keys(predefined) as [keyof typeof predefined]; + for (const name of keys) { t.notThrows(() => { validateConfig(predefined[name]); }, `Predefined config ${name} fails verification!`); @@ -30,12 +31,23 @@ test("invalidate config", (t) => { validateConfig({ PREFIX: "ckb", SCRIPTS: { + //@ts-expect-error ABC: {}, }, }); }); t.throws(() => { + //@ts-expect-error validateConfig(null); }); }); + +test("data2 works with ScriptConfig", (t) => { + t.notThrows(() => { + assertHashType("debugPath", "type"); + assertHashType("debugPath", "data"); + assertHashType("debugPath", "data1"); + assertHashType("debugPath", "data2"); + }); +}); diff --git a/packages/e2e-test/scripts/run.ts b/packages/e2e-test/scripts/run.ts index f9a9fbd81..784d81650 100644 --- a/packages/e2e-test/scripts/run.ts +++ b/packages/e2e-test/scripts/run.ts @@ -36,7 +36,7 @@ async function main() { mkdirSync(CKB_CWD, { recursive: true }); mkdirSync(LIGHT_CLIENT_CWD, { recursive: true }); - const ckbReleaseUrl = ckb.getReleaseUrl(); + const ckbReleaseUrl = ckb.getReleaseUrl({ version: "v0.111.0" }); const ckbDownloadDest = getDefaultDownloadDestination(ckbReleaseUrl); let ckbBinaryPath = ckb.findBinaryPath(ckbDownloadDest); @@ -102,6 +102,7 @@ async function main() { cwd: LIGHT_CLIENT_CWD, }); + // TODO uncomment me when the light client is available for CKB2023 const lightClientProcess = spawn( lightClientBinaryPath, ["run", "--config-file", join(LIGHT_CLIENT_CWD, "light-client.toml")], @@ -124,6 +125,7 @@ async function main() { console.info("Light Client started, tip header:", lightClientTip); + // TODO uncomment me when the light client is available for CKB2023 execSync("npx ava '**/*.e2e.test.ts' --verbose --timeout=5m", { cwd: pathTo("/"), stdio: "inherit", diff --git a/packages/e2e-test/tests/light-client-rpc.e2e.test.ts b/packages/e2e-test/tests/light-client-rpc.e2e.test.ts index c1504c57c..461b69158 100644 --- a/packages/e2e-test/tests/light-client-rpc.e2e.test.ts +++ b/packages/e2e-test/tests/light-client-rpc.e2e.test.ts @@ -156,13 +156,33 @@ test.before(async (t) => { }; }); -test("light client get_tip_header rpc", async (t) => { +test("light-client get_tip_header rpc", async (t) => { const tipHeader = await lightClientRPC.getTipHeader(); t.true(typeof tipHeader.dao === "string"); t.true(typeof tipHeader.number == "string"); t.true(typeof tipHeader.hash == "string"); }); +test("light-client get_peers rpc", async (t) => { + const peers = await lightClientRPC.getPeers(); + t.true(Array.isArray(peers)); + t.true(Array.isArray(peers[0].addresses)); + t.true(Array.isArray(peers[0].protocols)); + t.true(typeof peers[0].connectedDuration == "string"); + t.true(typeof peers[0].nodeId == "string"); + t.true(typeof peers[0].version == "string"); +}); + +test("light-client local_node_info rpc", async (t) => { + const nodeInfo = await lightClientRPC.localNodeInfo(); + t.true(typeof nodeInfo.active == "boolean"); + t.true(typeof nodeInfo.connections == "string"); + t.true(typeof nodeInfo.nodeId == "string"); + t.true(typeof nodeInfo.version == "string"); + t.true(Array.isArray(nodeInfo.addresses)); + t.true(Array.isArray(nodeInfo.protocols)); +}); + test("light-client get_genesis_block rpc", async (t) => { const res = await lightClientRPC.getGenesisBlock(); t.deepEqual(res.header.epoch, "0x0"); @@ -221,8 +241,8 @@ test.serial("light-client get_transactions rpc", async (t) => { ); t.deepEqual( - grouped.objects.map((o) => o.transaction), - ungrouped.objects.map((o) => o.transaction) + grouped.objects.map((o) => o.txHash), + ungrouped.objects.map((o) => o.txHash) ); }); @@ -235,10 +255,11 @@ test.serial("test setScripts", async (t) => { ); t.deepEqual(listeningBob, false); - await lightClientRPC.setScripts([ - ...beforeScripts, - { script: bob.lockScript, scriptType: "lock", blockNumber: "0x0" }, - ]); + // partial case + await lightClientRPC.setScripts( + [{ script: bob.lockScript, scriptType: "lock", blockNumber: "0x0" }], + "partial" + ); const afterScripts = await lightClientRPC.getScripts(); @@ -246,6 +267,19 @@ test.serial("test setScripts", async (t) => { isEqual(script.script, bob.lockScript) ); t.deepEqual(afterListeningBob, true); + + // delete case + await lightClientRPC.setScripts( + [{ script: bob.lockScript, scriptType: "lock", blockNumber: "0x0" }], + "delete" + ); + + const deletedAfterScripts = await lightClientRPC.getScripts(); + + const deletedAfterListeningBob = deletedAfterScripts.some((script) => + isEqual(script.script, bob.lockScript) + ); + t.deepEqual(deletedAfterListeningBob, false); }); test.serial("test lightClient getCells by Cellcollector", async (t) => { @@ -319,9 +353,11 @@ test.serial( const tx = await waitLightClientFetchTransaction(lightClientRPC, txHash); t.deepEqual(tx.transaction.hash, txHash); + t.true(typeof tx.txStatus.status == "string"); const gotTx = await lightClientRPC.getTransaction(txHash); t.deepEqual(gotTx.transaction.hash, txHash); + t.true(typeof tx.txStatus.status == "string"); } ); diff --git a/packages/hd-cache/tests/cache.test.ts b/packages/hd-cache/tests/cache.test.ts index 03b63a5c5..837c20ec5 100644 --- a/packages/hd-cache/tests/cache.test.ts +++ b/packages/hd-cache/tests/cache.test.ts @@ -55,6 +55,8 @@ const mockTxs: TransactionWithStatus[] = [ blockHash: "0x8df4763d10cf22509845f8ec728d56d1027d4dfe633cb91abf0d751ed5d45d68", }, + timeAddedToPool: null, + cycles: null, }, { transaction: { @@ -119,6 +121,8 @@ const mockTxs: TransactionWithStatus[] = [ blockHash: "0x8df4763d10cf22509845f8ec728d56d1027d4dfe633cb91abf0d751ed5d45d68", }, + timeAddedToPool: null, + cycles: null, }, ]; const headerData = { diff --git a/packages/helpers/src/address-to-script.ts b/packages/helpers/src/address-to-script.ts index edd0ce465..9cdb2f625 100644 --- a/packages/helpers/src/address-to-script.ts +++ b/packages/helpers/src/address-to-script.ts @@ -6,11 +6,11 @@ // | 0x02 | full version with hashType = "Data", deprecated | // | 0x04 | full version with hashType = "Type", deprecated | -import { Address, Script } from "@ckb-lumos/base"; +import { Address, Script, blockchain } from "@ckb-lumos/base"; +import { bytes } from "@ckb-lumos/codec"; import { getConfig } from "@ckb-lumos/config-manager"; import { bech32, bech32m } from "bech32"; import { Options } from "./"; -import { byteArrayToHex } from "./utils"; const BECH32_LIMIT = 1023; @@ -62,17 +62,9 @@ export function parseFullFormatAddress( throw new Error("Invalid payload length, too short!"); } - const codeHash = byteArrayToHex(body.slice(0, 32)); - const hashType = (() => { - const serializedHashType = body[32]; - - if (serializedHashType === 0) return "data"; - if (serializedHashType === 1) return "type"; - if (serializedHashType === 2) return "data1"; - - throw new Error(`Invalid hashType ${serializedHashType}`); - })(); - const args = byteArrayToHex(body.slice(33)); + const codeHash = bytes.hexify(body.slice(0, 32)); + const hashType = blockchain.HashType.unpack(body.slice(32, 33)); + const args = bytes.hexify(body.slice(33)); return { codeHash, hashType, args }; } @@ -112,7 +104,7 @@ export function parseDeprecatedCkb2019Address( return { codeHash: scriptTemplate.CODE_HASH, hashType: scriptTemplate.HASH_TYPE, - args: byteArrayToHex(argsBytes), + args: bytes.hexify(argsBytes), }; } // payload = 0x02 | codeHash | args @@ -121,9 +113,9 @@ export function parseDeprecatedCkb2019Address( throw Error(`Invalid payload length!`); } return { - codeHash: byteArrayToHex(body.slice(0, 32)), + codeHash: bytes.hexify(body.slice(0, 32)), hashType: "data", - args: byteArrayToHex(body.slice(32)), + args: bytes.hexify(body.slice(32)), }; } // payload = 0x04 | codeHash | args @@ -132,9 +124,9 @@ export function parseDeprecatedCkb2019Address( throw Error(`Invalid payload length!`); } return { - codeHash: byteArrayToHex(body.slice(0, 32)), + codeHash: bytes.hexify(body.slice(0, 32)), hashType: "type", - args: byteArrayToHex(body.slice(32)), + args: bytes.hexify(body.slice(32)), }; } } diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 6cf1d22fd..65cd071f3 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -23,6 +23,7 @@ import { } from "./address-to-script"; import { hexToByteArray } from "./utils"; import { validators } from "@ckb-lumos/toolkit"; +import { HashType } from "@ckb-lumos/base/lib/blockchain"; const { bytify, hexify } = bytes; export interface Options { @@ -237,22 +238,16 @@ export function encodeToAddress( ): Address { validators.ValidateScript(script); config = config || getConfig(); - - const data: number[] = []; - - const hashType = (() => { - if (script.hashType === "data") return 0; - if (script.hashType === "type") return 1; - if (script.hashType === "data1") return 2; - - /* c8 ignore next */ - throw new Error(`Invalid hashType ${script.hashType}`); - })(); - - data.push(0x00); - data.push(...hexToByteArray(script.codeHash)); - data.push(hashType); - data.push(...hexToByteArray(script.args)); + // https://github.com/nervosnetwork/rfcs/blob/9aef152a5123c8972de1aefc11794cf84d1762ed/rfcs/0021-ckb-address-format/0021-ckb-address-format.md#full-payload-format + // Full payload format directly encodes all data fields of lock script. The encode rule of full payload format is Bech32m. + // payload = 0x00 | code_hash | hash_type | args + + const data = bytes.concat( + [0x00], + script.codeHash, + HashType.pack(script.hashType), + script.args + ); return bech32m.encode(config.PREFIX, bech32m.toWords(data), BECH32_LIMIT); } diff --git a/packages/helpers/src/utils.ts b/packages/helpers/src/utils.ts index b2c021538..2d6fad7a4 100644 --- a/packages/helpers/src/utils.ts +++ b/packages/helpers/src/utils.ts @@ -14,7 +14,3 @@ export function hexToByteArray(h: HexString): number[] { } return array; } - -export function byteArrayToHex(a: number[]): HexString { - return "0x" + a.map((i) => ("00" + i.toString(16)).slice(-2)).join(""); -} diff --git a/packages/light-client/__tests__/formatters/result.fixtures.json b/packages/light-client/__tests__/formatters/result.fixtures.json new file mode 100644 index 000000000..2cbad557a --- /dev/null +++ b/packages/light-client/__tests__/formatters/result.fixtures.json @@ -0,0 +1,304 @@ +{ + "toFetchHeaderResult": [ + { + "result": null, + "expected": null + }, + { + "result": { + "status": "fetched", + "data": { + "compact_target": "0x1d0909cb", + "dao": "0x769f81bd06b75f493ec4978679a727003bc946cb2f1da20500ede26535a1c208", + "epoch": "0x7080484001dbc", + "extra_hash": "0x4cc0568a7dbfb65317d63de28d3410a38d359819f2097466fa4d9e6a9c40db2a", + "hash": "0x9b2862af26ac0d31e5b420dc2f880d6bf90578905d8575b9482521ae30a37438", + "nonce": "0xf94e93770aa18ac56779fabc42b84b06", + "number": "0xac0815", + "parent_hash": "0xd6927ff55611c799617431d0cfd9d8f7fccfa63e09bbcf75ef3bee9951d5c403", + "proposals_hash": "0xfc7bdc64909e322afa6076f50c52d87b004fb7685feebd1a02f6aca1666f08d2", + "timestamp": "0x18bc9556f20", + "transactions_root": "0x9305d2d8981ea3000dd4a9cf1fd88baa1ad25e9a51df81d25b76c204ba7546b6", + "version": "0x0" + } + }, + "expected": { + "status": "fetched", + "data": { + "compactTarget": "0x1d0909cb", + "dao": "0x769f81bd06b75f493ec4978679a727003bc946cb2f1da20500ede26535a1c208", + "epoch": "0x7080484001dbc", + "extraHash": "0x4cc0568a7dbfb65317d63de28d3410a38d359819f2097466fa4d9e6a9c40db2a", + "hash": "0x9b2862af26ac0d31e5b420dc2f880d6bf90578905d8575b9482521ae30a37438", + "nonce": "0xf94e93770aa18ac56779fabc42b84b06", + "number": "0xac0815", + "parentHash": "0xd6927ff55611c799617431d0cfd9d8f7fccfa63e09bbcf75ef3bee9951d5c403", + "proposalsHash": "0xfc7bdc64909e322afa6076f50c52d87b004fb7685feebd1a02f6aca1666f08d2", + "timestamp": "0x18bc9556f20", + "transactionsRoot": "0x9305d2d8981ea3000dd4a9cf1fd88baa1ad25e9a51df81d25b76c204ba7546b6", + "version": "0x0" + } + } + }, + { + "result": { + "status": "fetching", + "firstSent": "0" + }, + "expected": { + "status": "fetching", + "firstSent": "0" + } + }, + { + "result": { + "status": "added", + "timestamp": "0x18bc952c4b4" + }, + "expected": { + "status": "added", + "timestamp": "0x18bc952c4b4" + } + }, + { + "result": { + "status": "not_found" + }, + "expected": { + "status": "not_found" + } + } + ], + "toFetchTransactionResult": [ + { + "result": null, + "expected": null + }, + { + "result": { + "status": "fetched", + "data": { + "cycles": null, + "transaction": { + "cell_deps": [ + { + "dep_type": "code", + "out_point": { + "index": "0x0", + "tx_hash": "0x9154df4f7336402114d04495175b37390ce86a4906d2d4001cf02c3e6d97f39c" + } + }, + { + "dep_type": "dep_group", + "out_point": { + "index": "0x0", + "tx_hash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37" + } + }, + { + "dep_type": "code", + "out_point": { + "index": "0x0", + "tx_hash": "0xe12877ebd2c3c364dc46c5c992bcfaf4fee33fa13eebdf82c591fc9825aab769" + } + } + ], + "hash": "0x6517b90717d2e746603c89f6dbeb96beed6351607dd1d96bb61516723b4afbce", + "header_deps": [], + "inputs": [ + { + "previous_output": { + "index": "0x1", + "tx_hash": "0x8c6a46dda01bfce8c8ae650ec0b81feb08bc536cc7798240cecc163f3a0db424" + }, + "since": "0x0" + }, + { + "previous_output": { + "index": "0x1", + "tx_hash": "0x6976de183dac31af38555cfdbb23a9fae189ae91cee47e42a1c57f13bc0cb4b6" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "capacity": "0x9502f9000", + "lock": { + "args": "0x0a6d3bb9392242c0357fcae7ccead8f87b428ed280909bfdfaa549529b85df38a900000014000000340000009d000000a5000000c0a98ac6a990995e23e25c9267ac1ae2086fabc27f71e6d9d10624495a70420f690000001000000030000000310000008d6af470fd57d1afe543751eed58afef310ec659d565319f448bcc765176dcdc01340000000a6d3bb9392242c0357fcae7ccead8f87b428ed280909bfdfaa549529b85df383c079358bdf52af66dee6ca1bb637b44b1bd2345803a0900000000c002000000", + "code_hash": "0x9aa15280cf2123755516ff93180ab14b66a043562ffd70a0947afe7a12d573e5", + "hash_type": "type" + }, + "type": { + "args": "0x5c7253696786b9eddd34e4f6b6e478ec5742bd36569ec60c1d0487480ba4f9e3", + "code_hash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4", + "hash_type": "type" + } + }, + { + "capacity": "0x12a612f7ee", + "lock": { + "args": "0x013c079358bdf52af66dee6ca1bb637b44b1bd234500", + "code_hash": "0x79f90bb5e892d80dd213439eeab551120eb417678824f282b4ffb5f21bad2e1e", + "hash_type": "type" + }, + "type": null + } + ], + "outputs_data": [ + "0x40420f00000000000000000000000000", + "0x" + ], + "version": "0x0", + "witnesses": [ + "0x69000000100000006900000069000000550000005500000010000000550000005500000041000000b0e5b6170989927fba4c24b42dffd118f6579309edb591c54b78fc518df90be727aeb3e996eab62d0dfa005e2e8576c3a5681b409eeaf85118e38323a38a82bf00" + ] + }, + "tx_status": { + "block_hash": "0x04001f13f5686be48321fa04762c3095977fd59ecafd964a04df85a70e4388f6", + "status": "committed" + } + } + }, + "expected": { + "status": "fetched", + "data": { + "cycles": null, + "transaction": { + "cellDeps": [ + { + "depType": "code", + "outPoint": { + "index": "0x0", + "txHash": "0x9154df4f7336402114d04495175b37390ce86a4906d2d4001cf02c3e6d97f39c" + } + }, + { + "depType": "depGroup", + "outPoint": { + "index": "0x0", + "txHash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37" + } + }, + { + "depType": "code", + "outPoint": { + "index": "0x0", + "txHash": "0xe12877ebd2c3c364dc46c5c992bcfaf4fee33fa13eebdf82c591fc9825aab769" + } + } + ], + "hash": "0x6517b90717d2e746603c89f6dbeb96beed6351607dd1d96bb61516723b4afbce", + "headerDeps": [], + "inputs": [ + { + "previousOutput": { + "index": "0x1", + "txHash": "0x8c6a46dda01bfce8c8ae650ec0b81feb08bc536cc7798240cecc163f3a0db424" + }, + "since": "0x0" + }, + { + "previousOutput": { + "index": "0x1", + "txHash": "0x6976de183dac31af38555cfdbb23a9fae189ae91cee47e42a1c57f13bc0cb4b6" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "capacity": "0x9502f9000", + "lock": { + "args": "0x0a6d3bb9392242c0357fcae7ccead8f87b428ed280909bfdfaa549529b85df38a900000014000000340000009d000000a5000000c0a98ac6a990995e23e25c9267ac1ae2086fabc27f71e6d9d10624495a70420f690000001000000030000000310000008d6af470fd57d1afe543751eed58afef310ec659d565319f448bcc765176dcdc01340000000a6d3bb9392242c0357fcae7ccead8f87b428ed280909bfdfaa549529b85df383c079358bdf52af66dee6ca1bb637b44b1bd2345803a0900000000c002000000", + "codeHash": "0x9aa15280cf2123755516ff93180ab14b66a043562ffd70a0947afe7a12d573e5", + "hashType": "type" + }, + "type": { + "args": "0x5c7253696786b9eddd34e4f6b6e478ec5742bd36569ec60c1d0487480ba4f9e3", + "codeHash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4", + "hashType": "type" + } + }, + { + "capacity": "0x12a612f7ee", + "lock": { + "args": "0x013c079358bdf52af66dee6ca1bb637b44b1bd234500", + "codeHash": "0x79f90bb5e892d80dd213439eeab551120eb417678824f282b4ffb5f21bad2e1e", + "hashType": "type" + }, + "type": null + } + ], + "outputsData": [ + "0x40420f00000000000000000000000000", + "0x" + ], + "version": "0x0", + "witnesses": [ + "0x69000000100000006900000069000000550000005500000010000000550000005500000041000000b0e5b6170989927fba4c24b42dffd118f6579309edb591c54b78fc518df90be727aeb3e996eab62d0dfa005e2e8576c3a5681b409eeaf85118e38323a38a82bf00" + ] + }, + "txStatus": { + "blockHash": "0x04001f13f5686be48321fa04762c3095977fd59ecafd964a04df85a70e4388f6", + "status": "committed" + } + } + } + }, + { + "result": { + "status": "fetching", + "firstSent": "0" + }, + "expected": { + "status": "fetching", + "firstSent": "0" + } + }, + { + "result": { + "status": "added", + "timestamp": "0x18bc952c4b4" + }, + "expected": { + "status": "added", + "timestamp": "0x18bc952c4b4" + } + }, + { + "result": { + "status": "not_found" + }, + "expected": { + "status": "not_found" + } + } + ], + "toLightClientScript": [ + { + "result": null, + "expected": null + }, + { + "result": { + "block_number": "0x7d0", + "script": { + "args": "0x50878ce52a68feb47237c29574d82288f58b5d21", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "script_type": "lock" + }, + "expected": { + "blockNumber": "0x7d0", + "script": { + "args": "0x50878ce52a68feb47237c29574d82288f58b5d21", + "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hashType": "type" + }, + "scriptType": "lock" + } + } + ] +} \ No newline at end of file diff --git a/packages/light-client/__tests__/formatters/result.test.js b/packages/light-client/__tests__/formatters/result.test.js new file mode 100644 index 000000000..547268a2e --- /dev/null +++ b/packages/light-client/__tests__/formatters/result.test.js @@ -0,0 +1,21 @@ +const resultFmt = require('../../lib/resultFormatter') +const fixtures = require('./result.fixtures.json') + +describe('result formatter', () => { + describe.each(Object.keys(fixtures))('%s', methodName => { + const fixtureTable = Object.values(fixtures[methodName]).map(({ result, expected, exception }) => [ + result, + expected, + exception, + ]) + test.each(fixtureTable)('%j => %j', (result, expected, exception) => { + if (undefined !== expected) { + const formatted = resultFmt[methodName](result) + expect(formatted).toEqual(expected) + } + if (undefined !== exception) { + expect(resultFmt[methodName](result)).toThrow(new Error(exception)) + } + }) + }) +}) diff --git a/packages/light-client/package.json b/packages/light-client/package.json index c546d4e16..fb1d81968 100644 --- a/packages/light-client/package.json +++ b/packages/light-client/package.json @@ -28,11 +28,13 @@ "@ckb-lumos/testkit": "0.21.0-next.2", "@types/request": "^2.48.8", "@types/sinon": "^10.0.6", + "jest": "^28.1.3", "sinon": "^15.0.4" }, "scripts": { "fmt": "prettier --write \"{src,tests,examples}/**/*.ts\" package.json", "lint": "eslint -c ../../.eslintrc.js \"{src,tests,examples}/**/*.ts\"", + "test": "npx jest", "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", diff --git a/packages/light-client/src/resultFormatter.ts b/packages/light-client/src/resultFormatter.ts new file mode 100644 index 000000000..f9077e4e2 --- /dev/null +++ b/packages/light-client/src/resultFormatter.ts @@ -0,0 +1,51 @@ +/* eslint-disable camelcase, @typescript-eslint/no-explicit-any */ +import { + LightClientRPC, + FetchHeaderResult, + FetchTransactionResult, + LightClientScript, + FetchFlag, +} from "./type"; +import { ResultFormatter } from "@ckb-lumos/rpc"; + +export const toFetchHeaderResult = ( + result: LightClientRPC.FetchHeaderResult +): FetchHeaderResult => { + if (!result) return result; + + if (result.status === FetchFlag.Fetched) { + return { + status: result.status, + data: ResultFormatter.toHeader(result.data), + }; + } + + return result; +}; + +export const toFetchTransactionResult = ( + result: LightClientRPC.FetchTransactionResult +): FetchTransactionResult => { + if (!result) return result; + + if (result.status === FetchFlag.Fetched) { + return { + status: result.status, + data: ResultFormatter.toTransactionWithStatus(result.data), + }; + } + + return result; +}; + +export const toLightClientScript = ( + lightClientScript: LightClientRPC.LightClientScript +): LightClientScript => { + if (!lightClientScript) return lightClientScript; + + return { + script: ResultFormatter.toScript(lightClientScript.script), + blockNumber: lightClientScript.block_number, + scriptType: lightClientScript.script_type, + }; +}; diff --git a/packages/light-client/src/rpc.ts b/packages/light-client/src/rpc.ts index 8fa53a039..35ed42d2a 100644 --- a/packages/light-client/src/rpc.ts +++ b/packages/light-client/src/rpc.ts @@ -1,27 +1,18 @@ -import { HexString, utils, Header, Block } from "@ckb-lumos/base"; import { CKBComponents } from "@ckb-lumos/rpc/lib/types/api"; -import { ParamsFormatter } from "@ckb-lumos/rpc"; - -import { - GetLiveCellsResult, - Order, - SearchKey, - GetCellsSearchKey, - GetTransactionsSearchKey, -} from "@ckb-lumos/ckb-indexer/lib/type"; -import { - toScript, - toSearchKey, - toGetCellsSearchKey, - toGetTransactionsSearchKey, -} from "@ckb-lumos/ckb-indexer/lib/paramsFormatter"; -import { +import { RPC } from "@ckb-lumos/rpc/lib/types/rpc"; +import { ParamsFormatter, ResultFormatter } from "@ckb-lumos/rpc"; +import type { FetchHeaderResult, FetchTransactionResult, LightClientScript, - TransactionWithHeader, - LightClientTransactionList, + SetScriptCommand, + LightClientRPC as LightClientRPCType, } from "./type"; +import { + toFetchHeaderResult, + toFetchTransactionResult, + toLightClientScript, +} from "./resultFormatter"; import fetch from "cross-fetch"; /* c8 ignore next 100 */ @@ -32,99 +23,136 @@ export class LightClientRPC { */ constructor(private uri: string) {} - async getTipHeader(): Promise
{ - return utils.deepCamel(await request(this.uri, "get_tip_header")); + async getTipHeader(): Promise { + return ResultFormatter.toHeader(await request(this.uri, "get_tip_header")); + } + + async getPeers(): Promise> { + return ResultFormatter.toPeers(await request(this.uri, "get_peers")); + } + + async localNodeInfo(): Promise { + return ResultFormatter.toLocalNodeInfo( + await request(this.uri, "local_node_info") + ); } async fetchHeader(blockHash: string): Promise { const params = [blockHash]; - return utils.deepCamel(await request(this.uri, "fetch_header", params)); + return toFetchHeaderResult(await request(this.uri, "fetch_header", params)); } - async getHeader(blockHash: string): Promise
{ + async getHeader(blockHash: string): Promise { const params = [blockHash]; - return utils.deepCamel(await request(this.uri, "get_header", params)); + return ResultFormatter.toHeader( + await request(this.uri, "get_header", params) + ); } async fetchTransaction(txHash: string): Promise { const params = [txHash]; - return utils.deepCamel( + return toFetchTransactionResult( await request(this.uri, "fetch_transaction", params) ); } - async getTransaction(txHash: string): Promise { + async getTransaction( + txHash: string + ): Promise { const params = [txHash]; - return utils.deepCamel(await request(this.uri, "get_transaction", params)); + return ResultFormatter.toTransactionWithStatus( + await request(this.uri, "get_transaction", params) + ); } async sendTransaction( tx: CKBComponents.RawTransaction ): Promise { const params = [ParamsFormatter.toRawTransaction(tx)]; - return utils.deepCamel(await request(this.uri, "send_transaction", params)); + return ResultFormatter.toHash( + await request(this.uri, "send_transaction", params) + ); } async getScripts(): Promise> { - return utils.deepCamel(await request(this.uri, "get_scripts")); + return ( + await request<[], LightClientRPCType.LightClientScript[]>( + this.uri, + "get_scripts" + ) + ).map(toLightClientScript); } - async setScripts(scripts: Array): Promise { + async setScripts( + scripts: Array, + command?: SetScriptCommand + ): Promise { const params = [ scripts.map(({ script, scriptType, blockNumber }) => ({ - script: toScript(script), + script: ParamsFormatter.toScript(script), script_type: scriptType, block_number: blockNumber, })), + command, ]; - return utils.deepCamel(await request(this.uri, "set_scripts", params)); + await request(this.uri, "set_scripts", params); } async getCells( - searchKey: GetCellsSearchKey, - order: Order, - limit: HexString, + searchKey: CKBComponents.GetCellsSearchKey, + order: CKBComponents.Order, + limit: CKBComponents.UInt32, cursor?: string - ): Promise> { - const params = [toGetCellsSearchKey(searchKey), order, limit, cursor]; - return utils.deepCamel(await request(this.uri, "get_cells", params)); + ): Promise> { + const params = [ + ParamsFormatter.toGetCellsSearchKey(searchKey), + order, + limit, + cursor, + ]; + return ResultFormatter.toGetCellsResult( + await request(this.uri, "get_cells", params) + ); } async getCellsCapacity( - searchKey: SearchKey + searchKey: CKBComponents.SearchKey ): Promise { - const params = [toSearchKey(searchKey)]; - return utils.deepCamel( + const params = [ParamsFormatter.toSearchKey(searchKey)]; + return ResultFormatter.toCellsCapacity( await request(this.uri, "get_cells_capacity", params) ); } - async getGenesisBlock(): Promise { - return utils.deepCamel(await request(this.uri, "get_genesis_block")); + async getGenesisBlock(): Promise { + return ResultFormatter.toBlock( + await request<[], RPC.Block>(this.uri, "get_genesis_block") + ); } async getTransactions( - searchKey: GetTransactionsSearchKey, - order: Order, - limit: HexString, + searchKey: CKBComponents.GetTransactionsSearchKey, + order: CKBComponents.Order, + limit: CKBComponents.UInt32, cursor?: string - ): Promise> { + ): Promise> { const params = [ - toGetTransactionsSearchKey(searchKey), + ParamsFormatter.toGetTransactionsSearchKey(searchKey), order, limit, cursor, ]; - return utils.deepCamel(await request(this.uri, "get_transactions", params)); + return ResultFormatter.toGetTransactionsResult( + await request(this.uri, "get_transactions", params) + ); } } -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */ -const request = async ( +const request = async ( ckbIndexerUrl: string, method: string, - params?: any -): Promise => { + params?: P +): Promise => { const res = await fetch(ckbIndexerUrl, { method: "POST", body: JSON.stringify({ diff --git a/packages/light-client/src/type.ts b/packages/light-client/src/type.ts index 2c96802cb..582d9d596 100644 --- a/packages/light-client/src/type.ts +++ b/packages/light-client/src/type.ts @@ -1,6 +1,5 @@ -export type ScriptType = "type" | "lock"; - -import { HexNumber, Script, Transaction, Header } from "@ckb-lumos/base"; +import { CKBComponents } from "@ckb-lumos/rpc/lib/types/api"; +import { RPC } from "@ckb-lumos/rpc/lib/types/rpc"; export enum FetchFlag { Fetched = "fetched", @@ -9,53 +8,32 @@ export enum FetchFlag { NotFound = "not_found", } -export type FetchHeaderResult = - | { status: FetchFlag.Fetched; data: Header } +export type FetchResult = + | { status: FetchFlag.Fetched; data: R } | { status: FetchFlag.Fetching; firstSent: string } - | { status: FetchFlag.Added; timestamp: string } + | { status: FetchFlag.Added; timestamp: CKBComponents.Timestamp } | { status: FetchFlag.NotFound }; -export type TransactionWithHeader = { - transaction: Transaction; - header: Header; -}; - +export type FetchHeaderResult = FetchResult; export type FetchTransactionResult = - | { status: FetchFlag.Fetched; data: TransactionWithHeader } - | { status: FetchFlag.Fetching; firstSent: string } - | { status: FetchFlag.Added; timestamp: string } - | { status: FetchFlag.NotFound }; - -export interface LightClientTransactionList { - lastCursor: string | undefined; - objects: LightClientTransaction[]; -} + FetchResult; -export type LightClientTransaction = - Goruped extends true - ? GroupedLightClientTransaction - : UngroupedLightClientTransaction; +export type SetScriptCommand = "all" | "partial" | "delete"; -export type HexNum = string; -export type IOType = "input" | "output" | "both"; - -export type UngroupedLightClientTransaction = { - transaction: Transaction; - blockNumber: HexNum; - ioIndex: HexNum; - ioType: IOType; - txIndex: HexNum; +export type LightClientScript = { + script: CKBComponents.Script; + blockNumber: CKBComponents.UInt64; + scriptType: CKBComponents.ScriptType; }; -export type GroupedLightClientTransaction = { - transaction: Transaction; - blockNumber: HexNum; - txIndex: HexNum; - cells: Array<[IOType, HexNum]>; -}; +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace LightClientRPC { + export type FetchHeaderResult = FetchResult; + export type FetchTransactionResult = FetchResult; -export type LightClientScript = { - script: Script; - blockNumber: HexNumber; - scriptType: ScriptType; -}; + export type LightClientScript = { + script: RPC.Script; + block_number: RPC.Uint64; + script_type: RPC.ScriptType; + }; +} diff --git a/packages/rpc/__tests__/ckb-rpc-helpers.js b/packages/rpc/__tests__/ckb-rpc-helpers.js index 7946854c1..85607d63e 100644 --- a/packages/rpc/__tests__/ckb-rpc-helpers.js +++ b/packages/rpc/__tests__/ckb-rpc-helpers.js @@ -32,8 +32,8 @@ describe('ckb-rpc settings and helpers', () => { expect(rpc.node.httpsAgent).toBeDefined() }) - it('has 46 basic rpc', () => { - expect(Object.values(rpc)).toHaveLength(46) + it('has 47 basic rpc', () => { + expect(Object.values(rpc)).toHaveLength(47) }) it('set node url to http://test.localhost:8114', () => { diff --git a/packages/rpc/__tests__/ckb-rpc.test.js b/packages/rpc/__tests__/ckb-rpc.test.js index 2263ad920..e9b12bc08 100644 --- a/packages/rpc/__tests__/ckb-rpc.test.js +++ b/packages/rpc/__tests__/ckb-rpc.test.js @@ -691,7 +691,45 @@ describe("Test with mock", () => { "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e", epoch_duration_target: "0x3840", genesis_hash: - "0xeaa2c979898f80a12404578a9e1332d45c8ff2bf665457b10f9934203f230780", + "0x746f61610a0ea15713a568182365c0f4d8bcd2f61d42c91abc9279d4e858a190", + hardfork_features: [ + { + epoch_number: "0x0", + rfc: "0028", + }, + { + epoch_number: "0x0", + rfc: "0029", + }, + { + epoch_number: "0x0", + rfc: "0030", + }, + { + epoch_number: "0x0", + rfc: "0031", + }, + { + epoch_number: "0x0", + rfc: "0032", + }, + { + epoch_number: "0x0", + rfc: "0036", + }, + { + epoch_number: "0x0", + rfc: "0038", + }, + { + epoch_number: null, + rfc: "0048", + }, + { + epoch_number: null, + rfc: "0049", + }, + ], id: "ckb_dev", initial_primary_epoch_reward: "0xae6c73c3e070", max_block_bytes: "0x91c08", @@ -699,32 +737,47 @@ describe("Test with mock", () => { max_block_proposals_limit: "0x5dc", max_uncles_num: "0x2", median_time_block_count: "0x25", - orphan_rate_target: { denom: "0x28", numer: "0x1" }, + orphan_rate_target: { + denom: "0x28", + numer: "0x1", + }, permanent_difficulty_in_dummy: true, primary_epoch_reward_halving_interval: "0x2238", - proposer_reward_ratio: { denom: "0xa", numer: "0x4" }, + proposer_reward_ratio: { + denom: "0xa", + numer: "0x4", + }, secondary_epoch_reward: "0x37d0c8e28542", secp256k1_blake160_multisig_all_type_hash: "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8", secp256k1_blake160_sighash_all_type_hash: "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", - tx_proposal_window: { closest: "0x2", farthest: "0xa" }, + softforks: { + light_client: { + rfc0043: { + bit: 1, + min_activation_epoch: "0x0", + period: "0xa", + start: "0x0", + threshold: { + denom: "0x4", + numer: "0x3", + }, + timeout: "0x0", + }, + status: "rfc0043", + }, + }, + tx_proposal_window: { + closest: "0x2", + farthest: "0xa", + }, tx_version: "0x0", type_id_code_hash: "0x00000000000000000000000000000000000000000000000000545950455f4944", - hardfork_features: [ - { epoch_number: "0x0", rfc: "0028" }, - { epoch_number: "0x0", rfc: "0029" }, - { epoch_number: "0x0", rfc: "0030" }, - { epoch_number: "0x0", rfc: "0031" }, - { epoch_number: "0x0", rfc: "0032" }, - { epoch_number: "0x0", rfc: "0036" }, - { epoch_number: "0x0", rfc: "0038" }, - ], }, }, }); - const res = await rpc.getConsensus(); expect(JSON.parse(axiosMock.mock.calls[0][1].body)).toEqual({ id, @@ -739,7 +792,7 @@ describe("Test with mock", () => { "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e", epochDurationTarget: "0x3840", genesisHash: - "0xeaa2c979898f80a12404578a9e1332d45c8ff2bf665457b10f9934203f230780", + "0x746f61610a0ea15713a568182365c0f4d8bcd2f61d42c91abc9279d4e858a190", id: "ckb_dev", initialPrimaryEpochReward: "0xae6c73c3e070", maxBlockBytes: "0x91c08", @@ -761,14 +814,29 @@ describe("Test with mock", () => { typeIdCodeHash: "0x00000000000000000000000000000000000000000000000000545950455f4944", hardforkFeatures: [ - { epochNumber: "0x0", rfc: "0028" }, - { epochNumber: "0x0", rfc: "0029" }, - { epochNumber: "0x0", rfc: "0030" }, - { epochNumber: "0x0", rfc: "0031" }, - { epochNumber: "0x0", rfc: "0032" }, - { epochNumber: "0x0", rfc: "0036" }, - { epochNumber: "0x0", rfc: "0038" }, + { rfc: "0028", epochNumber: "0x0" }, + { rfc: "0029", epochNumber: "0x0" }, + { rfc: "0030", epochNumber: "0x0" }, + { rfc: "0031", epochNumber: "0x0" }, + { rfc: "0032", epochNumber: "0x0" }, + { rfc: "0036", epochNumber: "0x0" }, + { rfc: "0038", epochNumber: "0x0" }, + { rfc: "0048", epochNumber: null }, + { rfc: "0049", epochNumber: null }, ], + softforks: { + lightClient: { + status: "rfc0043", + rfc0043: { + bit: 1, + start: "0x0", + timeout: "0x0", + minActivationEpoch: "0x0", + period: "0xa", + threshold: { denom: "0x4", numer: "0x3" }, + }, + }, + }, }); }); @@ -1656,7 +1724,9 @@ describe("Test with mock", () => { { methodName: "getBlockFilter", result: null, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toBlockFilter), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toBlockFilter + ), requestParams: [ "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40", ], @@ -1798,7 +1868,9 @@ describe("Test with mock", () => { uncles: [], }, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toForkBlockResult), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toForkBlockResult + ), requestParams: [ "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94", ], @@ -1811,24 +1883,29 @@ describe("Test with mock", () => { }, { methodName: "getForkBlock", - result: '0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94', - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toForkBlockResult), + result: + "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94", + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toForkBlockResult + ), requestParams: [ "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94", - 0n + 0n, ], expectedParams: { method: "get_fork_block", params: [ "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94", - '0x0' + "0x0", ], }, }, { methodName: "getForkBlock", result: null, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toForkBlockResult), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toForkBlockResult + ), requestParams: [ "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94", ], @@ -1981,7 +2058,9 @@ describe("Test with mock", () => { mean: "0xe79d", median: "0x14a8", }, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toFeeRateStatistics), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toFeeRateStatistics + ), requestParams: [], expectedParams: { method: "get_fee_rate_statistics", @@ -1991,7 +2070,9 @@ describe("Test with mock", () => { { methodName: "getFeeRateStatistics", result: null, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toFeeRateStatistics), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toFeeRateStatistics + ), requestParams: [], expectedParams: { method: "get_fee_rate_statistics", @@ -2014,7 +2095,9 @@ describe("Test with mock", () => { { methodName: "getFeeRateStatics", result: null, - resultFormatter: ResultFormatter.toNullable(ResultFormatter.toFeeRateStatistics), + resultFormatter: ResultFormatter.toNullable( + ResultFormatter.toFeeRateStatistics + ), requestParams: [], expectedParams: { method: "get_fee_rate_statics", @@ -2040,11 +2123,6 @@ describe("Test with mock", () => { const res = await rpc[methodName](...requestParams); expect(res).toEqual(resultFormatter(result)); - console.log( - JSON.stringify(axiosMock.mock.calls[0][0].data), - "\n", - JSON.stringify({ id, jsonrpc: "2.0", ...expectedParams }) - ); expect(JSON.parse(axiosMock.mock.calls[0][1].body)).toEqual({ id, jsonrpc: "2.0", diff --git a/packages/rpc/__tests__/formatters/result.fixtures.json b/packages/rpc/__tests__/formatters/result.fixtures.json index 1be7b85eb..6e447bf23 100644 --- a/packages/rpc/__tests__/formatters/result.fixtures.json +++ b/packages/rpc/__tests__/formatters/result.fixtures.json @@ -615,6 +615,214 @@ "transactions": [], "uncles": [] } + }, + { + "result": "0x0e02000018000000e8000000ec000000e6010000ea0100000000000000000120691e8ccb8a01000004000000000000000000000400e8030000fa775972159aba682a5bf6e1ad6aa2a989c1ad1af61f130da4a2639a6dbfd782b6ce096ba31efabe2c387cbad8b3a455f8f6d2ade3204bd215291c2df9c88500000000000000000000000000000000000000000000000000000000000000006fd0305920429076102ae14371a11a338c9f6d7b35d022bc3a4cea771d50190f14896308581fa12ed1f84c9bf2862300cbab61de3e0000000099f54b01fbfe06ed97c68a255c08adc3120210350aad1004000000fa00000008000000f20000000c0000006c000000600000001c000000200000002400000028000000580000005c0000000000000000000000000000000100000004000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff040000000400000086000000080000007a0000007a0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d7210000000000000020302e3131312e3020286161613531353820323032332d30392d3134290000000020000000880b0d848373b0b419e66835223d3bd423c6f0f40a7a3864e10912a7230cc417", + "expected": "0x0e02000018000000e8000000ec000000e6010000ea0100000000000000000120691e8ccb8a01000004000000000000000000000400e8030000fa775972159aba682a5bf6e1ad6aa2a989c1ad1af61f130da4a2639a6dbfd782b6ce096ba31efabe2c387cbad8b3a455f8f6d2ade3204bd215291c2df9c88500000000000000000000000000000000000000000000000000000000000000006fd0305920429076102ae14371a11a338c9f6d7b35d022bc3a4cea771d50190f14896308581fa12ed1f84c9bf2862300cbab61de3e0000000099f54b01fbfe06ed97c68a255c08adc3120210350aad1004000000fa00000008000000f20000000c0000006c000000600000001c000000200000002400000028000000580000005c0000000000000000000000000000000100000004000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff040000000400000086000000080000007a0000007a0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d7210000000000000020302e3131312e3020286161613531353820323032332d30392d3134290000000020000000880b0d848373b0b419e66835223d3bd423c6f0f40a7a3864e10912a7230cc417" + }, + { + "result": { + "block": { + "extension": "0x7dc094baf449662901d17c12a6b03af16181537d83aff41689d65ebd90131d7b", + "header": { + "compact_target": "0x20010000", + "dao": "0x5c490f74f540a12eb334c0d0f8862300cac44a092c07000000755322c8fbfe06", + "epoch": "0x3e80096000000", + "extra_hash": "0xf6e4abc7a5f61ec64060e2956b972e3d8f450102fa677cb23ee49373c55f14d1", + "hash": "0xbfba299e8942c89f96357b811a795343c0db0f42dd913e22c4fbd74843ef4c5a", + "nonce": "0xa238c12f207efedc2c388a0f2f78fcb", + "number": "0x96", + "parent_hash": "0x03bb5df51f01aafa4aa3010f86909aaa7a90dc0681c264bb3712e5c02ac1935e", + "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "0x18ad0b37f4e", + "transactions_root": "0x4dbf22caeebbc7cd35e631689b94e6abb4bd10bf2c175b40d187f997b7acd02f", + "version": "0x0" + }, + "proposals": [], + "transactions": [ + { + "cell_deps": [], + "hash": "0x4efec35228be483cdf3d58d73829b129e7b9dc6f85fc715e9267f5adeb2326b9", + "header_deps": [], + "inputs": [ + { + "previous_output": { + "index": "0xffffffff", + "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "since": "0x96" + } + ], + "outputs": [ + { + "capacity": "0x2ecbd6b44c", + "lock": { + "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + } + ], + "outputs_data": ["0x"], + "version": "0x0", + "witnesses": [ + "0x7a0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d7210000000000000020302e3131312e3020286161613531353820323032332d30392d313429" + ] + }, + { + "cell_deps": [ + { + "dep_type": "dep_group", + "out_point": { + "index": "0x0", + "tx_hash": "0x5ee6d047719f30ba35065a99a13fbbd69dcf94b56273b23e77e8d0068e6e3984" + } + } + ], + "hash": "0x875371e951529d8b246736feea9033d09d3c5f1be4283644b2f49a43d5a5cdcd", + "header_deps": [], + "inputs": [ + { + "previous_output": { + "index": "0x7", + "tx_hash": "0x78420da5665c6bfa57abfcb58f3723223d79d55a3e1c771f6b433c72ac776ec9" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "capacity": "0x174876e800", + "lock": { + "args": "0xed17669081a0a2d854d6cc30d2c1c1912680bbe3", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + }, + { + "capacity": "0x1bc16d50064f9160", + "lock": { + "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + } + ], + "outputs_data": ["0x", "0x"], + "version": "0x0", + "witnesses": [ + "0x5500000010000000550000005500000041000000b0e9ea8ed8a50a12d518c565d57cd9baaa9b5bd64e314b721c7c5140c3663fe25149aab99e63667fca71d96ba2c18c80bcbab2a33f40879e9c8ff47a8ca3894a00" + ] + } + ], + "uncles": [] + }, + "cycles": ["0x18d041"] + }, + "expected": { + "cycles": ["0x18d041"], + "block": { + "header": { + "compactTarget": "0x20010000", + "parentHash": "0x03bb5df51f01aafa4aa3010f86909aaa7a90dc0681c264bb3712e5c02ac1935e", + "transactionsRoot": "0x4dbf22caeebbc7cd35e631689b94e6abb4bd10bf2c175b40d187f997b7acd02f", + "proposalsHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraHash": "0xf6e4abc7a5f61ec64060e2956b972e3d8f450102fa677cb23ee49373c55f14d1", + "dao": "0x5c490f74f540a12eb334c0d0f8862300cac44a092c07000000755322c8fbfe06", + "epoch": "0x3e80096000000", + "hash": "0xbfba299e8942c89f96357b811a795343c0db0f42dd913e22c4fbd74843ef4c5a", + "nonce": "0xa238c12f207efedc2c388a0f2f78fcb", + "number": "0x96", + "timestamp": "0x18ad0b37f4e", + "version": "0x0" + }, + "uncles": [], + "transactions": [ + { + "cellDeps": [], + "inputs": [ + { + "previousOutput": { + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "index": "0xffffffff" + }, + "since": "0x96" + } + ], + "outputs": [ + { + "lock": { + "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hashType": "type", + "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7" + }, + "type": null, + "capacity": "0x2ecbd6b44c" + } + ], + "outputsData": ["0x"], + "headerDeps": [], + "hash": "0x4efec35228be483cdf3d58d73829b129e7b9dc6f85fc715e9267f5adeb2326b9", + "version": "0x0", + "witnesses": [ + "0x7a0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d7210000000000000020302e3131312e3020286161613531353820323032332d30392d313429" + ] + }, + { + "cellDeps": [ + { + "outPoint": { + "txHash": "0x5ee6d047719f30ba35065a99a13fbbd69dcf94b56273b23e77e8d0068e6e3984", + "index": "0x0" + }, + "depType": "depGroup" + } + ], + "inputs": [ + { + "previousOutput": { + "txHash": "0x78420da5665c6bfa57abfcb58f3723223d79d55a3e1c771f6b433c72ac776ec9", + "index": "0x7" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "lock": { + "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hashType": "type", + "args": "0xed17669081a0a2d854d6cc30d2c1c1912680bbe3" + }, + "type": null, + "capacity": "0x174876e800" + }, + { + "lock": { + "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hashType": "type", + "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7" + }, + "type": null, + "capacity": "0x1bc16d50064f9160" + } + ], + "outputsData": ["0x", "0x"], + "headerDeps": [], + "hash": "0x875371e951529d8b246736feea9033d09d3c5f1be4283644b2f49a43d5a5cdcd", + "version": "0x0", + "witnesses": [ + "0x5500000010000000550000005500000041000000b0e9ea8ed8a50a12d518c565d57cd9baaa9b5bd64e314b721c7c5140c3663fe25149aab99e63667fca71d96ba2c18c80bcbab2a33f40879e9c8ff47a8ca3894a00" + ] + } + ], + "extension": "0x7dc094baf449662901d17c12a6b03af16181537d83aff41689d65ebd90131d7b", + "proposals": [] + } + } } ], "toAlertMessage": [ @@ -1623,7 +1831,9 @@ "block_hash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "proof": { "indices": ["0x0"], - "lemmas": ["0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"] + "lemmas": [ + "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ] }, "witnesses_root": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" }, @@ -1631,7 +1841,9 @@ "blockHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "proof": { "indices": ["0x0"], - "lemmas": ["0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"] + "lemmas": [ + "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ] }, "witnessesRoot": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" } @@ -1649,6 +1861,17 @@ "dao_type_hash": null, "epoch_duration_target": "0x3840", "genesis_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed", + "hardfork_features": [ + { "rfc": "0028", "epoch_number": "0x1526" }, + { "rfc": "0029", "epoch_number": "0x0" }, + { "rfc": "0030", "epoch_number": "0x0" }, + { "rfc": "0031", "epoch_number": "0x0" }, + { "rfc": "0032", "epoch_number": "0x0" }, + { "rfc": "0036", "epoch_number": "0x0" }, + { "rfc": "0038", "epoch_number": "0x0" }, + { "rfc": "0048", "epoch_number": null }, + { "rfc": "0049", "epoch_number": null } + ], "id": "main", "initial_primary_epoch_reward": "0x71afd498d000", "max_block_bytes": "0x91c08", @@ -1669,6 +1892,22 @@ "secondary_epoch_reward": "0x37d0c8e28542", "secp256k1_blake160_multisig_all_type_hash": null, "secp256k1_blake160_sighash_all_type_hash": null, + "softforks": { + "testdummy": { + "status": "rfc0043", + "rfc0043": { + "bit": 1, + "min_activation_epoch": "0x0", + "period": "0xa", + "start": "0x0", + "threshold": { + "denom": "0x4", + "numer": "0x3" + }, + "timeout": "0x0" + } + } + }, "tx_proposal_window": { "closest": "0x2", "farthest": "0xa" @@ -1682,6 +1921,17 @@ "daoTypeHash": null, "epochDurationTarget": "0x3840", "genesisHash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed", + "hardforkFeatures": [ + { "rfc": "0028", "epochNumber": "0x1526" }, + { "rfc": "0029", "epochNumber": "0x0" }, + { "rfc": "0030", "epochNumber": "0x0" }, + { "rfc": "0031", "epochNumber": "0x0" }, + { "rfc": "0032", "epochNumber": "0x0" }, + { "rfc": "0036", "epochNumber": "0x0" }, + { "rfc": "0038", "epochNumber": "0x0" }, + { "rfc": "0048", "epochNumber": null }, + { "rfc": "0049", "epochNumber": null } + ], "id": "main", "initialPrimaryEpochReward": "0x71afd498d000", "maxBlockBytes": "0x91c08", @@ -1702,98 +1952,28 @@ "secondaryEpochReward": "0x37d0c8e28542", "secp256k1Blake160MultisigAllTypeHash": null, "secp256k1Blake160SighashAllTypeHash": null, - "txProposalWindow": { - "closest": "0x2", - "farthest": "0xa" - }, - "txVersion": "0x0", - "typeIdCodeHash": "0x00000000000000000000000000000000000000000000000000545950455f4944" - } - }, - { - "result": { - "block_version": "0x0", - "cellbase_maturity": "0x10000000000", - "dao_type_hash": null, - "epoch_duration_target": "0x3840", - "genesis_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed", - "id": "main", - "initial_primary_epoch_reward": "0x71afd498d000", - "max_block_bytes": "0x91c08", - "max_block_cycles": "0xd09dc300", - "max_block_proposals_limit": "0x5dc", - "max_uncles_num": "0x2", - "median_time_block_count": "0x25", - "orphan_rate_target": { - "denom": "0x28", - "numer": "0x1" - }, - "permanent_difficulty_in_dummy": false, - "primary_epoch_reward_halving_interval": "0x2238", - "proposer_reward_ratio": { - "denom": "0xa", - "numer": "0x4" - }, - "secondary_epoch_reward": "0x37d0c8e28542", - "secp256k1_blake160_multisig_all_type_hash": null, - "secp256k1_blake160_sighash_all_type_hash": null, - "tx_proposal_window": { - "closest": "0x2", - "farthest": "0xa" - }, - "tx_version": "0x0", - "type_id_code_hash": "0x00000000000000000000000000000000000000000000000000545950455f4944", - "hardfork_features": [ - { "epoch_number": "0x0", "rfc": "0028" }, - { "epoch_number": "0x0", "rfc": "0029" }, - { "epoch_number": "0x0", "rfc": "0030" }, - { "epoch_number": "0x0", "rfc": "0031" }, - { "epoch_number": "0x0", "rfc": "0032" }, - { "epoch_number": "0x0", "rfc": "0036" }, - { "epoch_number": "0x0", "rfc": "0038" } - ] - }, - "expected": { - "blockVersion": "0x0", - "cellbaseMaturity": "0x10000000000", - "daoTypeHash": null, - "epochDurationTarget": "0x3840", - "genesisHash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed", - "id": "main", - "initialPrimaryEpochReward": "0x71afd498d000", - "maxBlockBytes": "0x91c08", - "maxBlockCycles": "0xd09dc300", - "maxBlockProposalsLimit": "0x5dc", - "maxUnclesNum": "0x2", - "medianTimeBlockCount": "0x25", - "orphanRateTarget": { - "denom": "0x28", - "numer": "0x1" - }, - "permanentDifficultyInDummy": false, - "primaryEpochRewardHalvingInterval": "0x2238", - "proposerRewardRatio": { - "denom": "0xa", - "numer": "0x4" + "softforks": { + "testdummy": { + "status": "rfc0043", + "rfc0043": { + "bit": 1, + "minActivationEpoch": "0x0", + "period": "0xa", + "start": "0x0", + "threshold": { + "denom": "0x4", + "numer": "0x3" + }, + "timeout": "0x0" + } + } }, - "secondaryEpochReward": "0x37d0c8e28542", - "secp256k1Blake160MultisigAllTypeHash": null, - "secp256k1Blake160SighashAllTypeHash": null, "txProposalWindow": { "closest": "0x2", "farthest": "0xa" }, "txVersion": "0x0", - "typeIdCodeHash": "0x00000000000000000000000000000000000000000000000000545950455f4944", - "hardforkFeatures": [ - { "epochNumber": "0x0", "rfc": "0028" }, - { "epochNumber": "0x0", "rfc": "0029" }, - { "epochNumber": "0x0", "rfc": "0030" }, - { "epochNumber": "0x0", "rfc": "0031" }, - { "epochNumber": "0x0", "rfc": "0032" }, - { "epochNumber": "0x0", "rfc": "0036" }, - { "epochNumber": "0x0", "rfc": "0038" } - ] + "typeIdCodeHash": "0x00000000000000000000000000000000000000000000000000545950455f4944" } } ], @@ -1803,8 +1983,14 @@ "expected": null }, { - "result": { "proposed": ["proposed_1", "proposed_2"], "pending": ["pending_1", "pending_2"] }, - "expected": { "proposed": ["proposed_1", "proposed_2"], "pending": ["pending_1", "pending_2"] } + "result": { + "proposed": ["proposed_1", "proposed_2"], + "pending": ["pending_1", "pending_2"] + }, + "expected": { + "proposed": ["proposed_1", "proposed_2"], + "pending": ["pending_1", "pending_2"] + } }, { "result": { @@ -1943,11 +2129,11 @@ "last_cursor": "0x", "objects": [ { - "block_number": "0x1", - "io_index": "0x1", - "io_type": "output", - "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "tx_index": "0x4" + "block_number": "0x1", + "io_index": "0x1", + "io_type": "output", + "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "tx_index": "0x4" }, { "block_number": "0x2", @@ -1962,11 +2148,11 @@ "lastCursor": "0x", "objects": [ { - "blockNumber": "0x1", - "ioIndex": "0x1", - "ioType": "output", - "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "txIndex": "0x4" + "blockNumber": "0x1", + "ioIndex": "0x1", + "ioType": "output", + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "txIndex": "0x4" }, { "blockNumber": "0x2", diff --git a/packages/rpc/src/Base/index.ts b/packages/rpc/src/Base/index.ts index 4df2b8037..5e6f3e7ae 100644 --- a/packages/rpc/src/Base/index.ts +++ b/packages/rpc/src/Base/index.ts @@ -21,6 +21,28 @@ export const rpcProperties: RpcPropertes = { // skip subscription }; +// prettier-ignore +interface GetBlock { + (query:Q, verbosity?: '0x2', withCycle?: false): Promise; + (query:Q, verbosity: '0x0', withCycle?: false): Promise; + (query:Q, verbosity: '0x0', withCycle: true): Promise<{ block:string, cycles:CKBComponents.UInt64 }>; + (query:Q, verbosity: '0x2', withCycle: true): Promise<{ block: CKBComponents.BlockView; cycles: CKBComponents.UInt64 }>; +} + +// prettier-ignore +export interface GetTransaction { + (hash: CKBComponents.Hash): Promise; + (hash: CKBComponents.Hash, verbosity: "0x0", onlyCommitted?: boolean): Promise>; + (hash: CKBComponents.Hash, verbosity: "0x1", onlyCommitted?: boolean): Promise>; + (hash: CKBComponents.Hash, verbosity: "0x2", onlyCommitted?: boolean): Promise; +} + +// prettier-ignore +export interface GetHeader { + (query: Q, verbosity?: "0x1"): Promise; + (query: Q, verbosity: "0x0"): Promise; +} + export interface Base { /* Chain */ @@ -72,10 +94,12 @@ export interface Base { * @method getBlock * @memberof DefaultRPC * @description rpc to get block by its hash - * @param {string} hash - the block hash of the target block - * @returns {Promise} block object + * @param {string} hash + * @param {string} verbosity + * @param {boolean} withCycle + * @return {Promise} */ - getBlock: (hash: CKBComponents.Hash) => Promise; + getBlock: GetBlock; /** * @method getHeader @@ -83,9 +107,7 @@ export interface Base { * @description Returns the information about a block header by hash. * @params {Promise} block hash */ - getHeader: ( - blockHash: CKBComponents.Hash - ) => Promise; + getHeader: GetHeader; /** * @method getHeaderByNumber @@ -93,9 +115,7 @@ export interface Base { * @description Returns the information about a block header by block number * @params {Promise} block number */ - getHeaderByNumber: ( - blockNumber: CKBComponents.BlockNumber | bigint - ) => Promise; + getHeaderByNumber: GetHeader; /** * @method getLiveCell @@ -121,9 +141,7 @@ export interface Base { * @param {string} hash - the transaction hash of the target transaction * @return {Promise} transaction object with transaction status */ - getTransaction: ( - hash: CKBComponents.Hash - ) => Promise; + getTransaction: GetTransaction; /** * @method getCellbaseOutputCapacityDetails @@ -183,13 +201,13 @@ export interface Base { /** * @method getBlockByNumber * @memberof DefaultRPC - * @description rpc to get block by its number - * @param {string} number - the block number of the target block - * @returns {Promise} block object + * @description rpc to get block by its hash + * @param {CKBComponents.BlockNumber | bigint} number + * @param {string} verbosity + * @param {boolean} withCycle + * @return {Promise} */ - getBlockByNumber: ( - number: CKBComponents.BlockNumber | bigint - ) => Promise; + getBlockByNumber: GetBlock; /* Experimental */ @@ -511,7 +529,11 @@ export interface Base { * @param target Specify the number (1 - 101) of confirmed blocks to be counted. If the number is even, automatically add one. If not specified, defaults to 2 * @returns the feeRate statistics of confirmed blocks on the chain If the query finds the corresponding historical data, the corresponding statistics are returned, containing the mean and median, in shannons per kilo-weight. If not, it returns null. */ - getFeeRateStatistics: () => Promise; + getFeeRateStatistics: ( + target?: CKBComponents.UInt64 + ) => Promise; + + getDeploymentsInfo: () => Promise; } export class Base { diff --git a/packages/rpc/src/Base/stats.ts b/packages/rpc/src/Base/stats.ts index a28d45e44..91639b4b3 100644 --- a/packages/rpc/src/Base/stats.ts +++ b/packages/rpc/src/Base/stats.ts @@ -6,4 +6,9 @@ export default { paramsFormatters: [], resultFormatters: resultFmts.toBlockchainInfo, }, + getDeploymentsInfo: { + method: "get_deployments_info", + paramsFormatters: [], + resultFormatters: resultFmts.toDeploymentsInfo, + }, }; diff --git a/packages/rpc/src/method.ts b/packages/rpc/src/method.ts index a46edd9f3..947fcdebe 100644 --- a/packages/rpc/src/method.ts +++ b/packages/rpc/src/method.ts @@ -62,9 +62,7 @@ export class Method { if (res.error) { throw new ResponseException(JSON.stringify(res.error)); } - return ( - this.#options.resultFormatters?.(res.result) ?? res.result - ); + return this.#options.resultFormatters?.(res.result) ?? res.result; }); clearTimeout(timeout); diff --git a/packages/rpc/src/resultFormatter.ts b/packages/rpc/src/resultFormatter.ts index bdd534085..f4c765116 100644 --- a/packages/rpc/src/resultFormatter.ts +++ b/packages/rpc/src/resultFormatter.ts @@ -24,7 +24,13 @@ const toNullable = const toNumber = (number: RPC.BlockNumber): CKBComponents.BlockNumber => number.toString(); const toHash = (hash: RPC.Hash256): CKBComponents.Hash256 => hash; -const toHeader = (header: RPC.Header): CKBComponents.BlockHeader => { + +function toHeader(header: RPC.Header): CKBComponents.BlockHeader; +function toHeader(header: string): string; +function toHeader( + header: string | RPC.Header +): string | CKBComponents.BlockHeader { + if (typeof header === "string") return header; if (!header) return header; const { compact_target: compactTarget, @@ -42,7 +48,8 @@ const toHeader = (header: RPC.Header): CKBComponents.BlockHeader => { extraHash, ...rest, }; -}; +} + const toScript = (script: RPC.Script): CKBComponents.Script => { if (!script) return script; const { code_hash: codeHash, hash_type: hashType, ...rest } = script; @@ -99,7 +106,7 @@ function toTransaction(tx: RPC.Transaction): CKBComponents.Transaction; function toTransaction( tx: RPC.RawTransaction | RPC.Transaction ): CKBComponents.Transaction | CKBComponents.RawTransaction { - if (!tx) return tx; + if (!tx || typeof tx !== "object") return tx; const { cell_deps: cellDeps = [], inputs = [], @@ -131,16 +138,29 @@ const toTip = (tip: RPC.Tip): CKBComponents.Tip => ({ blockNumber: tip.block_number, }); -const toBlock = (block: RPC.Block): CKBComponents.Block => { - if (!block) return block; - const { header, uncles = [], transactions = [], ...rest } = block; +type BlockWithCycles = { block: RPC.Block | string; cycles: string[] }; +function toBlock(block: string): string; +function toBlock(block: RPC.Block): CKBComponents.Block; +function toBlock(block: T): T; +function toBlock(res: string | RPC.Block | BlockWithCycles): any { + if (!res) return res; + if (typeof res === "string") return res; + + if ("block" in res && "cycles" in res) { + return { + cycles: res.cycles, + block: toBlock(res.block as any), + }; + } + + const { header, uncles = [], transactions = [], ...rest } = res; return { header: toHeader(header), uncles: uncles.map(toUncleBlock), transactions: transactions.map(toTransaction), ...rest, }; -}; +} const toAlertMessage = ( alertMessage: RPC.AlertMessage ): CKBComponents.AlertMessage => { @@ -298,20 +318,26 @@ const toCellsIncludingOutPoint = ( if (!Array.isArray(cells)) return []; return cells.map(toCellIncludingOutPoint); }; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -const toTransactionWithStatus = (txWithStatus: RPC.TransactionWithStatus) => { +const toTransactionWithStatus = ( + txWithStatus: RPC.TransactionWithStatus +): CKBComponents.TransactionWithStatus => { if (!txWithStatus) return txWithStatus; const { transaction, tx_status: { block_hash: blockHash, status }, + time_added_to_pool, ...rest } = txWithStatus; return { - transaction: toTransaction(transaction), + transaction: toTransaction(transaction) as Tx, txStatus: { blockHash, status, + ...("reason" in txWithStatus.tx_status + ? { reason: txWithStatus.tx_status.reason } + : {}), }, + timeAddedToPool: time_added_to_pool, ...rest, }; }; @@ -503,8 +529,47 @@ const toTransactionProof = ( ...rest, }; }; + +const toHardforkFeature = ( + feature: RPC.HardforkFeature +): CKBComponents.HardForkFeature => { + return { + rfc: feature.rfc, + epochNumber: feature.epoch_number, + }; +}; + +const toDeployment = (deployment: RPC.Deployment): CKBComponents.Deployment => { + return { + bit: deployment.bit, + start: deployment.start, + timeout: deployment.timeout, + minActivationEpoch: deployment.min_activation_epoch, + period: deployment.period, + threshold: deployment.threshold, + }; +}; + +const toSoftFork = (softFork: RPC.SoftFork): CKBComponents.SoftFork => { + if ("rfc0043" in softFork) { + return { + status: softFork.status, + rfc0043: toDeployment(softFork.rfc0043), + }; + } + return softFork; +}; + const toConsensus = (consensus: RPC.Consensus): CKBComponents.Consensus => { if (!consensus) return consensus; + + const rpcHardforkFeatures = consensus.hardfork_features; + + const softforks = consensus.softforks; + const lightClient = + softforks.light_client && toSoftFork(softforks.light_client); + const testdummy = softforks.testdummy && toSoftFork(softforks.testdummy); + return { blockVersion: consensus.block_version, cellbaseMaturity: consensus.cellbase_maturity, @@ -531,13 +596,11 @@ const toConsensus = (consensus: RPC.Consensus): CKBComponents.Consensus => { txProposalWindow: consensus.tx_proposal_window, txVersion: consensus.tx_version, typeIdCodeHash: consensus.type_id_code_hash, - hardforkFeatures: - consensus.hardfork_features?.map( - ({ epoch_number: epochNumber, ...rest }) => ({ - epochNumber, - ...rest, - }) - ) ?? consensus.hardfork_features, + hardforkFeatures: rpcHardforkFeatures.map(toHardforkFeature), + softforks: { + ...(lightClient && { lightClient }), + ...(testdummy && { testdummy }), + }, }; }; @@ -586,11 +649,13 @@ const toIndexerCell = ( }; }; -const toGetCellsResult = ( +const toGetCellsResult = ( getCellsResult: RPC.GetLiveCellsResult -): CKBComponents.GetLiveCellsResult => ({ +): CKBComponents.GetLiveCellsResult => ({ lastCursor: getCellsResult.last_cursor, - objects: getCellsResult.objects.map((object) => toIndexerCell(object)), + objects: getCellsResult.objects.map((object) => + toIndexerCell(object) + ) as CKBComponents.GetLiveCellsResult["objects"], }); const isUngroupedIndexerTransaction = ( @@ -696,6 +761,7 @@ const toForkBlockResult = ( uncles: result.uncles.map(toUncleBlock), transactions: result.transactions.map(toTransaction), proposals: result.proposals, + extension: result.extension, }; }; @@ -707,6 +773,46 @@ const toEstimateCycles = ( }; }; +const toDeployState = ( + state: RPC.DeploymentState +): CKBComponents.DeploymentState => { + if (state === "locked_in") { + return "lockedIn"; + } + return state; +}; + +const toDeploymentInfo = ( + deploymentInfo: RPC.DeploymentInfo +): CKBComponents.DeploymentInfo => { + return { + bit: deploymentInfo.bit, + /// specifies the first epoch in which the bit gains meaning. + start: deploymentInfo.start, + timeout: deploymentInfo.timeout, + minActivationEpoch: deploymentInfo.min_activation_epoch, + period: deploymentInfo.period, + threshold: deploymentInfo.threshold, + since: deploymentInfo.since, + state: toDeployState(deploymentInfo.state), + }; +}; + +const toDeploymentsInfo = ( + deploymentInfo: RPC.DeploymentsInfo +): CKBComponents.DeploymentsInfo => { + const { light_client, testdummy } = deploymentInfo.deployments; + + return { + hash: deploymentInfo.hash, + epoch: deploymentInfo.epoch, + deployments: { + ...(light_client ? { lightClient: toDeploymentInfo(light_client) } : {}), + ...(testdummy ? { testdummy: toDeploymentInfo(testdummy) } : {}), + }, + }; +}; + export { toNumber, toHash, @@ -760,5 +866,9 @@ export { toFeeRateStatistics, toForkBlockResult, toEstimateCycles, + toDeployment, + toDeployState, + toDeploymentInfo, + toDeploymentsInfo, }; /* eslint-enable camelcase */ diff --git a/packages/rpc/src/types/api.ts b/packages/rpc/src/types/api.ts index dc3dca127..60385c92f 100644 --- a/packages/rpc/src/types/api.ts +++ b/packages/rpc/src/types/api.ts @@ -1,4 +1,5 @@ import type * as api from "@ckb-lumos/base"; + /** * @see https://github.com/nervosnetwork/ckb/blob/develop/protocol/src/protocol.fbs for more infGomation */ @@ -10,6 +11,7 @@ export namespace CKBComponents { export type Hash256 = string; export type UInt32 = string; export type UInt64 = string; + export type U256 = string; export type Index = string; export type Version = string; @@ -27,13 +29,14 @@ export namespace CKBComponents { export type RationalU256 = Record<"denom" | "numer", string>; export type ProposalWindow = Record<"closest" | "farthest", BlockNumber>; export type EpochNumberWithFraction = string; + export type EpochNumber = string; export enum TransactionStatus { Pending = "pending", Proposed = "proposed", Committed = "committed", } - export type ScriptHashType = "data" | "type" | "data1"; + export type ScriptHashType = api.HashType; export type DepType = "code" | "depGroup"; export type JsonBytes = string; @@ -69,8 +72,9 @@ export namespace CKBComponents { export type CellDep = api.CellDep; export type RawTransaction = api.RawTransaction & { witnesses: Witness[] }; export type Transaction = Required; - export type TransactionWithStatus = api.TransactionWithStatus; - export type BlockHeader = api.Header; + export type TransactionWithStatus = + api.TransactionWithStatus; + export type BlockHeader = T; export type Block = api.Block; export type UncleBlock = api.UncleBlock; export type LiveCell = api.LiveCell; @@ -90,6 +94,14 @@ export namespace CKBComponents { export type TxPoolVerbosity = api.TxPoolVerbosity; export type RawTxPool = api.RawTxPool; export type Consensus = api.Consensus; + export type HardForks = api.HardForks; + export type HardForkFeature = api.HardforkFeature; + export type SoftForkStatus = api.SoftForkStatus; + export type SoftFork = api.SoftFork; + export type Buried = api.Buried; + export type Rfc0043 = api.Rfc0043; + export type Ratio = api.Ratio; + export type Deployment = api.Deployment; export type QueryOptions = api.QueryOptions; export interface TransactionPoint { @@ -291,6 +303,7 @@ export namespace CKBComponents { uncles: UncleBlock[]; transactions: TransactionView[]; proposals: ProposalShortId[]; + extension: Hash; } export type SerializedBlock = api.HexString; @@ -303,4 +316,9 @@ export namespace CKBComponents { export interface EstimateCycles { cycles: UInt64; } + + export type DeploymentPos = api.DeploymentPos; + export type DeploymentState = api.DeploymentState; + export type DeploymentInfo = api.DeploymentInfo; + export type DeploymentsInfo = api.DeploymentsInfo; } diff --git a/packages/rpc/src/types/rpc.ts b/packages/rpc/src/types/rpc.ts index 54dc256f1..3eaff27ba 100644 --- a/packages/rpc/src/types/rpc.ts +++ b/packages/rpc/src/types/rpc.ts @@ -9,6 +9,8 @@ export namespace RPC { export type ProposalShortId = CKBComponents.ProposalShortId; export type Number = CKBComponents.Number; export type UInt32 = CKBComponents.UInt32; + export type Uint64 = CKBComponents.UInt64; + export type U256 = CKBComponents.U256; export type Count = CKBComponents.Count; export type DAO = CKBComponents.DAO; export type Hash = CKBComponents.Hash; @@ -30,17 +32,25 @@ export namespace RPC { export type EpochNumberWithFraction = CKBComponents.EpochNumberWithFraction; export type JsonBytes = CKBComponents.JsonBytes; export type IOType = CKBComponents.IOType; + export type EpochNumber = CKBComponents.EpochNumber; + + // this is a type to mapping the `HashMap`, `BTreeMap` in `jsonrpc-types` + // there are some returns of CKB RPC are in this format, like `Softfork` + type MapLike = { [key in K]?: V }; + type Vec = T[]; export enum TransactionStatus { Pending = "pending", Proposed = "proposed", Committed = "committed", + Unknown = "unknown", + Rejected = "rejected", } - export type ScriptHashType = CKBComponents.ScriptHashType; - export type DepType = "code" | "dep_group"; + export type ScriptHashType = CKBComponents.ScriptHashType; + export interface Script { args: Bytes; code_hash: Hash256; @@ -110,8 +120,15 @@ export namespace RPC { } | { block_hash: undefined; - status: TransactionStatus.Pending | TransactionStatus.Proposed; + status: + | TransactionStatus.Pending + | TransactionStatus.Proposed + | TransactionStatus.Rejected + | TransactionStatus.Unknown; + reason?: string; }; + time_added_to_pool: Uint64 | null; + cycles: Uint64 | null; } export interface TransactionPoint { @@ -169,14 +186,10 @@ export namespace RPC { message: string; } - export interface BlockchainInfo { - is_initial_block_download: boolean; - epoch: string; - difficulty: string; - median_time: string; - chain: string; - alerts: AlertMessage[]; - } + /** + * @deprecated please migrate to {@link ChainInfo} + */ + export type BlockchainInfo = ChainInfo; export interface LocalNodeInfo { active: boolean; @@ -322,7 +335,6 @@ export namespace RPC { export interface Consensus { id: string; genesis_hash: Hash256; - hardfork_features: Array<{ rfc: string; epoch_number: string | undefined }>; dao_type_hash: Hash256 | undefined; secp256k1_blake160_sighash_all_type_hash: Hash256 | undefined; secp256k1_blake160_multisig_all_type_hash: Hash256 | undefined; @@ -343,6 +355,44 @@ export namespace RPC { max_block_proposals_limit: string; primary_epoch_reward_halving_interval: string; permanent_difficulty_in_dummy: boolean; + hardfork_features: HardForks; + softforks: { [key in DeploymentPos]?: SoftFork }; + } + + export type HardForks = Array; + + export interface HardforkFeature { + rfc: string; + epoch_number: EpochNumber | null; + } + + export type SoftFork = Buried | Rfc0043; + + export interface Buried { + status: SoftForkStatus; + active: boolean; + epoch: EpochNumber; + } + + export interface Rfc0043 { + status: SoftForkStatus; + rfc0043: Deployment; + } + + export type SoftForkStatus = "buried" | "rfc0043"; + + export interface Ratio { + numer: Uint64; + denom: Uint64; + } + + export interface Deployment { + bit: number; + start: EpochNumber; + timeout: EpochNumber; + min_activation_epoch: EpochNumber; + period: EpochNumber; + threshold: Ratio; } export interface Tip { @@ -445,6 +495,7 @@ export namespace RPC { uncles: UncleBlockView[]; transactions: TransactionView[]; proposals: ProposalShortId[]; + extension: Hash256; } export type SerializedBlock = HexString; @@ -452,6 +503,40 @@ export namespace RPC { export interface EstimateCycles { cycles: HexNumber; } -} -/* eslint-enable camelcase */ + /* https://github.com/nervosnetwork/ckb/blob/develop/util/jsonrpc-types/src/info.rs */ + export type DeploymentPos = "testdummy" | "light_client"; + + export type DeploymentState = + | "defined" + | "started" + | "locked_in" + | "active" + | "failed"; + + export interface DeploymentsInfo { + hash: Hash256; + epoch: EpochNumber; + deployments: MapLike; + } + + export interface DeploymentInfo { + bit: number; + start: EpochNumber; + timeout: EpochNumber; + min_activation_epoch: EpochNumber; + period: EpochNumber; + threshold: Ratio; + since: EpochNumber; + state: DeploymentState; + } + + export interface ChainInfo { + chain: string; + median_time: Timestamp; + epoch: EpochNumberWithFraction; + difficulty: U256; + is_initial_block_download: boolean; + alerts: Vec; + } +} diff --git a/packages/runner/src/ckb/config-dev.ts b/packages/runner/src/ckb/config-dev.ts index fb23145b7..0bfaabc98 100644 --- a/packages/runner/src/ckb/config-dev.ts +++ b/packages/runner/src/ckb/config-dev.ts @@ -80,7 +80,8 @@ export function generateConfigSync( // TODO add typescript description here // eslint-disable-next-line @typescript-eslint/no-explicit-any const ckbMinerToml: Record = parseToml(source); - ckbMinerToml.miner.workers[0].value = 500n; + ckbMinerToml.miner.client.poll_interval = 100n; + ckbMinerToml.miner.workers[0].value = 100n; ckbMinerToml.miner.client.rpc_url = `http://127.0.0.1:${cmd.config.rpcPort}`; return stringifyToml(ckbMinerToml, { newline: "\n" }); diff --git a/packages/runner/src/ckb/get.ts b/packages/runner/src/ckb/get.ts index fbea25538..54567826a 100644 --- a/packages/runner/src/ckb/get.ts +++ b/packages/runner/src/ckb/get.ts @@ -12,7 +12,7 @@ export interface GetReleaseUrlOptions { */ export function getReleaseUrl(options: GetReleaseUrlOptions = {}): string { const { - version = "v0.109.0", + version = "v0.111.0", arch = os.arch(), platform = os.platform(), } = options; diff --git a/packages/runner/src/light-client/get.ts b/packages/runner/src/light-client/get.ts index 98541ffa2..a6683e494 100644 --- a/packages/runner/src/light-client/get.ts +++ b/packages/runner/src/light-client/get.ts @@ -10,7 +10,7 @@ export interface Options { * @param options */ export function getReleaseUrl(options: Options = {}): string { - const { version = "v0.2.4", platform = os.platform() } = options; + const { version = "v0.3.0", platform = os.platform() } = options; const compiledArch: string = (() => "x86_64")(); diff --git a/packages/testkit/package.json b/packages/testkit/package.json index e4d44b490..77abef41b 100644 --- a/packages/testkit/package.json +++ b/packages/testkit/package.json @@ -21,6 +21,7 @@ "@ckb-lumos/base": "0.21.0-next.2", "@ckb-lumos/bi": "0.21.0-next.2", "@ckb-lumos/codec": "0.21.0-next.2", + "@ckb-lumos/rpc": "0.21.0-next.2", "@types/body-parser": "^1.19.1", "@types/download": "^8.0.1", "@types/express": "^4.17.17", diff --git a/packages/testkit/src/ckb-indexer-helper.ts b/packages/testkit/src/ckb-indexer-helper.ts index 7de4bd61c..0b811c476 100644 --- a/packages/testkit/src/ckb-indexer-helper.ts +++ b/packages/testkit/src/ckb-indexer-helper.ts @@ -3,6 +3,7 @@ import compareVersions from "compare-versions"; import os from "os"; // TODO dep import downloadAndExtract from "download"; +import childProcess from "node:child_process"; function log(info: string): void { console.log(info); @@ -85,10 +86,7 @@ export async function startCKBIndexer(CKBVersion?: string): Promise { await downloadCKBIndexer(); console.log("start indexer at", new Date().toLocaleString()); - shell.exec( - `RUST_LOG=info ./ckb-indexer -c http://127.0.0.1:8118/rpc -l 127.0.0.1:8120 -s indexer-store-tmp`, - { - async: true, - } + childProcess.exec( + `RUST_LOG=info ./ckb-indexer -c http://127.0.0.1:8118/rpc -l 127.0.0.1:8120 -s indexer-store-tmp` ); } diff --git a/packages/testkit/src/mock-ckb-jsonrpc.ts b/packages/testkit/src/mock-ckb-jsonrpc.ts index 810bfc470..e67ddca87 100644 --- a/packages/testkit/src/mock-ckb-jsonrpc.ts +++ b/packages/testkit/src/mock-ckb-jsonrpc.ts @@ -1,11 +1,14 @@ import { JSONRPCResponse, JSONRPCServer } from "json-rpc-2.0"; import express, { Express } from "express"; import bodyParser from "body-parser"; -import { LocalNode, Block, utils, blockchain } from "@ckb-lumos/base"; +import { blockchain, LocalNode, utils } from "@ckb-lumos/base"; import { bytes } from "@ckb-lumos/codec"; +import { RPC } from "@ckb-lumos/rpc/lib/types/rpc"; +import TransactionStatus = RPC.TransactionStatus; + interface Options { // FIXME it actually is RPCBlock - blocks: Block[]; + blocks: RPC.Block[]; localNode: LocalNode; // defaults to /rpc routePath?: string; @@ -76,24 +79,35 @@ export function createCKBMockRPC(options: Options): Express { return blocks[blocks.length - 1].header.number; }); - server.addMethod("get_transaction", (hashes) => { - assertsParams(Array.isArray(hashes)); - const hash = hashes[0]; - let result; - let blockHash; - for (const block of blocks) { - const tx = block.transactions.find((tx) => tx.hash === hash); - if (tx) { - result = tx; - blockHash = block.header.hash; - break; + server.addMethod( + "get_transaction", + (hashes): RPC.TransactionWithStatus | null => { + assertsParams(Array.isArray(hashes)); + const hash = hashes[0]; + let result: RPC.Transaction | undefined = undefined; + let blockHash: string | undefined = undefined; + for (const block of blocks) { + block.transactions; + const tx = block.transactions.find((tx) => tx.hash === hash); + if (tx) { + result = tx; + blockHash = block.header.hash; + break; + } } + if (!result || !blockHash) return null; + + return { + transaction: result, + tx_status: { + status: TransactionStatus.Committed, + block_hash: blockHash, + }, + time_added_to_pool: null, + cycles: null, + }; } - return { - transaction: result, - tx_status: { status: "pending", block_hash: blockHash }, - }; - }); + ); server.addMethod("get_blockchain_info", () => { return { diff --git a/packages/toolkit/src/normalizers.js b/packages/toolkit/src/normalizers.js index a26923f3d..ea33b619d 100644 --- a/packages/toolkit/src/normalizers.js +++ b/packages/toolkit/src/normalizers.js @@ -66,7 +66,7 @@ function normalizeObject(debugPath, object, keys) { for (const [key, f] of Object.entries(keys)) { const value = object[key]; - if (!value) { + if (value == null) { throw new Error(`${debugPath} is missing ${key}!`); } result[key] = f(`${debugPath}.${key}`, value); @@ -87,12 +87,16 @@ export function NormalizeScript(script, { debugPath = "script" } = {}) { return 1; case "data1": return 2; + case "data2": + return 4; case 0: return value; case 1: return value; case 2: return value; + case 4: + return value; default: throw new Error(`${debugPath}.hashType has invalid value: ${value}`); } diff --git a/packages/toolkit/src/validators.js b/packages/toolkit/src/validators.js index 0453ee2f5..38b7171aa 100644 --- a/packages/toolkit/src/validators.js +++ b/packages/toolkit/src/validators.js @@ -74,7 +74,8 @@ export function ValidateScript( if ( script.hashType !== "data" && script.hashType !== "type" && - script.hashType !== "data1" + script.hashType !== "data1" && + script.hashType !== "data2" ) { throw new Error(`${debugPath}.hashType must be either data or type!`); } diff --git a/packages/toolkit/tests/normailizers.js b/packages/toolkit/tests/normailizers.js index 7ff3475dd..a2f56050c 100644 --- a/packages/toolkit/tests/normailizers.js +++ b/packages/toolkit/tests/normailizers.js @@ -30,3 +30,23 @@ test("error outPoint should not pass validation", (t) => { }); }); }); + +test("normalizeScript should work", (t) => { + ["type", "data", "data1", "data2", 0, 1, 2, 4].forEach((hashType) => { + normalizers.NormalizeScript({ + codeHash: `0x${"00".repeat(32)}`, + args: "0x", + hashType, + }); + }); + + t.pass(); + + t.throws(() => { + normalizers.NormalizeScript({ + codeHash: `0x${"00".repeat(32)}`, + args: "0x", + hashType: "unknown", + }); + }); +}); diff --git a/packages/toolkit/types/api.d.ts b/packages/toolkit/types/api.d.ts index 94885cd1f..e8e8013d6 100644 --- a/packages/toolkit/types/api.d.ts +++ b/packages/toolkit/types/api.d.ts @@ -4,11 +4,11 @@ export type HexNumber = Hexadecimal; export type PackedSince = string; export type PackedDao = string; - + export type Address = string; - + export type HexadecimalRange = [Hexadecimal, Hexadecimal]; - + export interface Header { timestamp: HexNumber; number: HexNumber; @@ -24,7 +24,7 @@ export interface Header { version: HexNumber; } -export type HashType = "type" | "data" | "data1"; +export type HashType = "type" | "data" | "data1" | "data2"; export interface Script { codeHash: Hash; hashType: HashType; diff --git a/packages/transaction-manager/examples/chained-transfer-example/package.json b/packages/transaction-manager/examples/chained-transfer-example/package.json index fd3a8a66e..6e4b94a6f 100644 --- a/packages/transaction-manager/examples/chained-transfer-example/package.json +++ b/packages/transaction-manager/examples/chained-transfer-example/package.json @@ -15,14 +15,14 @@ "typescript": "^5.0.4" }, "dependencies": { - "@ckb-lumos/base": "0.20.0", - "@ckb-lumos/bi": "0.20.0", - "@ckb-lumos/ckb-indexer": "0.20.0", - "@ckb-lumos/codec": "0.20.0", - "@ckb-lumos/common-scripts": "0.20.0", - "@ckb-lumos/config-manager": "0.20.0", - "@ckb-lumos/hd": "0.20.0", - "@ckb-lumos/helpers": "0.20.0", - "@ckb-lumos/transaction-manager": "0.20.0" + "@ckb-lumos/base": "canary", + "@ckb-lumos/bi": "canary", + "@ckb-lumos/ckb-indexer": "canary", + "@ckb-lumos/codec": "canary", + "@ckb-lumos/common-scripts": "canary", + "@ckb-lumos/config-manager": "canary", + "@ckb-lumos/hd": "canary", + "@ckb-lumos/helpers": "canary", + "@ckb-lumos/transaction-manager": "canary" } } diff --git a/packages/utils/.changeset/README.md b/packages/utils/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/packages/utils/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/packages/utils/.changeset/config.json b/packages/utils/.changeset/config.json deleted file mode 100644 index c4bac441e..000000000 --- a/packages/utils/.changeset/config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", - "changelog": ["@devtools/changelog", { "repo": "ckb-js/lumos" }], - "commit": false, - "fixed": [["@ckb-lumos/*"]], - "linked": [], - "access": "public", - "baseBranch": "develop", - "updateInternalDependencies": "patch", - "ignore": [] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5af9064b4..a6552b88b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -521,6 +521,9 @@ importers: '@types/sinon': specifier: ^10.0.6 version: 10.0.6 + jest: + specifier: ^28.1.3 + version: 28.1.3(@types/node@20.1.0)(ts-node@10.9.1) sinon: specifier: ^15.0.4 version: 15.0.4 @@ -680,6 +683,9 @@ importers: '@ckb-lumos/codec': specifier: 0.21.0-next.2 version: link:../codec + '@ckb-lumos/rpc': + specifier: 0.21.0-next.2 + version: link:../rpc '@types/body-parser': specifier: ^1.19.1 version: 1.19.1 diff --git a/website/src/components/address-conversion/parseMultiVersionAddress.ts b/website/src/components/address-conversion/parseMultiVersionAddress.ts index 377b833c0..d3f512d58 100644 --- a/website/src/components/address-conversion/parseMultiVersionAddress.ts +++ b/website/src/components/address-conversion/parseMultiVersionAddress.ts @@ -13,7 +13,7 @@ export function parseMultiVersionAddress( | undefined; const ckb2021 = helpers.encodeToAddress(script, { config }); - if (script.hashType === "data1") { + if (script.hashType === "data1" || script.hashType === 'data2') { return { name, script,