diff --git a/config/config.ts b/config/config.ts index 8b265b9..8064660 100644 --- a/config/config.ts +++ b/config/config.ts @@ -1,21 +1,31 @@ import {config, helpers, Indexer, RPC} from "@ckb-lumos/lumos"; import {LightClient} from "../service/node"; import {LightClientCli} from "../service/light-cli"; +import {LightClientRPC} from "@ckb-lumos/light-client"; const CKB_CLIENT_CLI_PATH = "tmp/ckb-cli-light-client" const CKB_LIGHT_CLIENT_PATH = "tmp/startBlockchain/ckbLightClient/ckb-light-client" -const RPC_DEBUG = false +const DEV_PATH = "tmp/startBlockchain/ckbDevWithIndexAndeLightClient" +const CKB_DEV_PATH = "tmp/startBlockchain/ckbDevWithIndexAndeLightClient/ckb/target/release" +const CKB_DEV_INDEX_PATH = "tmp/startBlockchain/ckbDevWithIndexAndeLightClient/ckb-indexer/target/release" +const CKB_DEV_LIGHT_CLIENT_PATH = "tmp/startBlockchain/ckbDevWithIndexAndeLightClient/ckb-light-client/target/release" + +const CKB_DEV_RPC_URL = "http://localhost:8114"; +const CKB_DEV_RPC_INDEX_URL = "http://localhost:8116"; + +const RPC_DEBUG = true const CKB_RPC_URL = "https://testnet.ckb.dev"; +// const CKB_RPC_URL = CKB_DEV_RPC_URL; const CKB_RPC_INDEX_URL = "https://testnet.ckb.dev/indexer"; +// const CKB_RPC_INDEX_URL = CKB_DEV_RPC_INDEX_URL; const CKB_LIGHT_RPC_URL = "http://localhost:9000"; - export enum FeeRate { SLOW = 1000, - NORMAL = 100000, + NORMAL = 10000, FAST = 10000000 } -const uint = 100000000 +const uint = 100000000 const FEE = FeeRate.NORMAL config.initializeConfig( @@ -28,21 +38,25 @@ const MINER_SCRIPT = helpers.parseAddress("ckt1qyqvjdmh4re8t7mfjr0v0z27lwwjqu384 const MINER_SCRIPT2 = helpers.parseAddress("ckt1qyq8ph2ywxpvkl5l0rcsugcnwcfswqpqngeqqmfuwq") const MINER_SCRIPT3 = helpers.parseAddress("ckt1qyqd5eyygtdmwdr7ge736zw6z0ju6wsw7rssu8fcve") const CkbClientNode = new LightClient(CKB_LIGHT_CLIENT_PATH) +const lightClientRPC = new LightClientRPC(CKB_LIGHT_RPC_URL) const deprecatedAddr = helpers.generateAddress(script); const newFullAddr = helpers.encodeToAddress(script); const rpcCLient = new RPC(CKB_RPC_URL); +const rpcDevCLient = new RPC(CKB_DEV_RPC_URL); + const {AGGRON4} = config.predefined; const RPC_NETWORK = AGGRON4; const ACCOUNT_PRIVATE = "0xdd50cac37ec6dd12539a968c1a2cbedda75bd8724f7bcad486548eaabb87fc8b" const ACCOUNT_PRIVATE2 = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" const indexer = new Indexer(CKB_RPC_INDEX_URL, CKB_RPC_URL); -const LightCli = new LightClientCli(CKB_CLIENT_CLI_PATH,CKB_LIGHT_RPC_URL) +const LightCli = new LightClientCli(CKB_CLIENT_CLI_PATH, CKB_LIGHT_RPC_URL) +const indexerMockLightRpc = new LightClientRPC( CKB_RPC_INDEX_URL) const EVERY_ONE_CAN_PAY_DATA = { "CODE_HASH": '0xe683b04139344768348499c23eb1326d5a52d6db006c0d2fece00a831f3660d7', - "HASH_TYPE":"data", + "HASH_TYPE": "data", "TX_HASH": '0xbe5d236f316f5608c53aa682351be4114db07b2e843435e843661ded30924c04', "INDEX": '0x0', "DEP_TYPE": 'code' @@ -50,7 +64,7 @@ const EVERY_ONE_CAN_PAY_DATA = { const EVERY_ONE_CAN_PAY_DATA1 = { "CODE_HASH": '0xe683b04139344768348499c23eb1326d5a52d6db006c0d2fece00a831f3660d7', - "HASH_TYPE":"data1", + "HASH_TYPE": "data1", "TX_HASH": '0xbe5d236f316f5608c53aa682351be4114db07b2e843435e843661ded30924c04', "INDEX": '0x0', "DEP_TYPE": 'code' @@ -86,6 +100,14 @@ export { ACCOUNT_PRIVATE2, LightCli, EVERY_ONE_CAN_PAY_TYPE_ID, - EVERY_ONE_CAN_PAY_DATA - + EVERY_ONE_CAN_PAY_DATA, + CKB_DEV_PATH, + rpcDevCLient, + CKB_DEV_RPC_URL, + CKB_DEV_RPC_INDEX_URL, + DEV_PATH, + CKB_DEV_INDEX_PATH, + CKB_DEV_LIGHT_CLIENT_PATH, + lightClientRPC, + indexerMockLightRpc } diff --git a/package.json b/package.json index bcfb522..40ee4ac 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "node": ">=10" }, "dependencies": { - "@ckb-lumos/lumos": "0.18.0-rc7", + "@ckb-lumos/light-client": "^0.20.0-alpha.0", + "@ckb-lumos/lumos": "0.19.0", + "@ckb-lumos/codec": "0.19.0", "dotenv": "^16.0.1", "mochawesome-merge": "^4.2.1", "npm-run-all": "^4.1.5" @@ -19,6 +21,7 @@ "@types/chai": "^4.2.6", "@types/mocha": "^5.2.7", "chai": "^4.2.0", + "ckb-js-toolkit": "^0.10.2", "decimal.js": "^10.2.1", "mocha": "^9.2.2", "mocha-chai-jest-snapshot": "^1.1.0", @@ -31,15 +34,18 @@ "ts-generator": "^0.1.1", "ts-node": "^8.5.4", "typechain": "^4.0.0", - "typescript": "^3.7.3", - "ckb-js-toolkit": "^0.10.2" - + "typescript": "^3.7.3" }, "scripts": { "test": "mocha --config test/runners/mocha/.mocharc.jsonc --reporter mochawesome", "test-load": "mocha --config test/runners/mocha/.mocharc.load.jsonc --reporter mochawesome", "test-data": "mocha --config test/runners/mocha/.mocharc.data.jsonc --reporter mochawesome", "test-cli": "mocha --config test/runners/mocha/.mocharc.cli.jsonc --reporter mochawesome", - "test-transfer-issue": "mocha --config test/runners/mocha/.mocharc.issue.jsonc --reporter mochawesome" + "test-transfer-issue": "mocha --config test/runners/mocha/.mocharc.issue.jsonc --reporter mochawesome", + "test-index-panic": "mocha --config test/runners/mocha/.mocharc.index-panic.jsonc --reporter mochawesome", + "test-roll-back": "mocha --config test/runners/mocha/.mocharc.light.rollback.jsonc --reporter mochawesome", + "test-roll-back:transfer": "mocha --config test/runners/mocha/.mocharc.light.rollback.jsonc --grep \"transfer\"--reporter mochawesome", + "test-roll-back:cut10": "mocha --config test/runners/mocha/.mocharc.light.rollback.jsonc --grep \"cut 10\"--reporter mochawesome", + "test-roll-back:cut300": "mocha --config test/runners/mocha/.mocharc.light.rollback.jsonc --grep \"cut 300\"--reporter mochawesome" } } diff --git a/prepare.sh b/prepare.sh index 4954e93..fbcdbb2 100644 --- a/prepare.sh +++ b/prepare.sh @@ -6,7 +6,7 @@ sh prepare.sh sh start.sh sh status.sh cat ckb-light-client/target/release/node.log -echo "prepare ckbLightClient finish" +echo "prepare ckb Light Client finish" cd ../../ git clone https://github.com/TheWaWaR/ckb-cli-light-client.git cd ckb-cli-light-client diff --git a/resource/cli/example-search-key.withFilter.json b/resource/cli/example-search-key.withFilter.json index 473faad..3da66af 100644 --- a/resource/cli/example-search-key.withFilter.json +++ b/resource/cli/example-search-key.withFilter.json @@ -1,30 +1,25 @@ { - "script": { - "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", - "hash_type": "type", - "args": "0x00010203" - }, - "script_type": "lock", "filter": { - "script": { - "code_hash": "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e", - "hash_type": "type", - "args": "0x00010203" - }, - "script_len_range": null, - "output_data_len_range": [ - "0x16", - "0x378" - ], - "output_capacity_range": [ - "0xf4240", - "0x5f5e100" - ], "block_range": [ "0x21", "0x3e7" + ], + "script": { + "args": "0x00010203", + "code_hash": "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e", + "hash_type": "type" + }, + "script_len_range": [ + "0x0", + "0x6f" ] }, - "with_data": false, - "group_by_transaction": null + "group_by_transaction": false, + "script": { + "args": "0x00010203", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "script_type": "lock", + "with_data": false } diff --git a/rpc/index.ts b/rpc/index.ts index 61cc63e..981fba7 100644 --- a/rpc/index.ts +++ b/rpc/index.ts @@ -1,337 +1,363 @@ -import {Cell, HexNumber, OutPoint} from "@ckb-lumos/base"; -import {request} from "../service/index"; -import {IndexerTransaction, IndexerTransactionList, SearchKeyFilter, Terminator} from "../service/type"; -import {CKB_LIGHT_RPC_URL, indexer, rpcCLient} from "../config/config"; -import {BI, Indexer} from "@ckb-lumos/lumos"; -import {Hexadecimal, HexString} from "@ckb-lumos/base/lib/primitive"; -import {fetchTransactionUntilFetched} from "../service/txService"; - -const ckbLightClientRPC = CKB_LIGHT_RPC_URL; - -const DefaultTerminator: Terminator = () => { - return {stop: false, push: true}; -}; - -// const script = { -// code_hash: -// "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", -// hash_type: "type", -// args: "0x2760d76d61cafcfc1a83d9d3d6b70c36fa9d4b1a" +// import {Cell} from "@ckb-lumos/base"; +// import {request} from "../service/index"; +// import {IndexerTransaction, IndexerTransactionList, SearchKeyFilter, Terminator} from "../service/type"; +// import {CKB_LIGHT_RPC_URL, indexer, rpcCLient} from "../config/config"; +// import {BI, Indexer} from "@ckb-lumos/lumos"; +// import {Hexadecimal, HexNumber, HexString} from "@ckb-lumos/base/lib/primitive"; +// import {fetchTransactionUntilFetched} from "../service/txService"; +// import {LightClientRPC} from "@ckb-lumos/light-client"; +// import {GetCellsSearchKey, Order} from "@ckb-lumos/ckb-indexer/lib/type"; +// import {Script} from "@ckb-lumos/base/lib/api"; +// +// const ckbLightClientRPC = CKB_LIGHT_RPC_URL; +// +// const DefaultTerminator: Terminator = () => { +// return {stop: false, push: true}; // }; -export interface ScriptMsg { - script: ScriptObject - script_type: "lock" | "type" - block_number: string -} - - -export interface ScriptObject { - code_hash: string; - hash_type: string | null | undefined; - args: string | null | undefined; -} - -//{ -// script, -// script_type: script_type -// }, -// "asc", -// "0x6400000", -export interface SearchKey { - script: ScriptObject, - script_type: "lock" | "type" - filter?: SearchFilter - with_data?: boolean -} - -export interface GetTransactionsSearchKey { - script: ScriptObject, - script_type: "lock" | "type" - filter?: SearchFilter - group_by_transaction?: boolean -} - -export interface SearchFilter { - script?: ScriptObject - script_len_range?: HexString[] - output_data_len_range?: HexString[] - output_capacity_range?: HexString[] - block_range?: HexString[] -} - -export interface GetCellsRequest { - search_key: SearchKey - order: "asc" | "desc" - limit: HexString - after_cursor?: HexString -} - -export interface GetCellsCapacityReq { - search_key: SearchKey -} - -/** - * @description: set_scripts - * @param {script{code_hash,hash_type,args}} - * @return {any} - */ - -export async function setScripts(scripts: ScriptMsg[]) { - const res = await request(1, ckbLightClientRPC, "set_scripts", [ - scripts - ]); - return res; -} - -/** - * @description: get_tip_header - * @param {[]} - * @return {header} - */ -export async function getTipHeader(ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "get_tip_header", []); - return res; -} - -/** - * @description: get_scripts - * @return {any} - * @param ckbLightClient - */ -export async function getScripts(ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "get_scripts", []); - return res; -} - -export async function waitScriptsUpdate(block_num: BI, ckbLightClient: string = ckbLightClientRPC) { - while (true) { - let res = await getScripts(ckbLightClient) - if (res.length == 0) { - return - } - // get lowest blocker - let lower_block_num = BI.from("0xffffffffff") - for (let i = 0; i < res.length; i++) { - if (lower_block_num.gt(res[i].block_number)) { - lower_block_num = BI.from(res[i].block_number); - } - } - console.log('[waitScriptsUpdate] current get script Height:', lower_block_num.toNumber(), ",wait sync height:", block_num.toNumber(),) - - if (block_num.lte(lower_block_num)) { - return - } - await sleep(3000) - } -} - -export async function getCellsCapacityRequest(getCellsReq: GetCellsCapacityReq, ckbLightClient: string = ckbLightClientRPC) { - - let request1 = [ - getCellsReq.search_key]; - const res = await request(2, ckbLightClient, "get_cells_capacity", request1); - return { - block_hash: res.block_hash, - block_number: res.block_number, - capacity: res.capacity - } -} - -export async function getCellsCapacity(script?: ScriptObject, ckbLightClient: string = ckbLightClientRPC, script_type = "lock") { - - let get_cells_capacity_request = { - script: script, - script_type: script_type, - // filter?:filter, - } - const res = await request(2, ckbLightClient, "get_cells_capacity", [get_cells_capacity_request]); - console.log('res:', res) - return res -} - -//[ -// { -// "script": { -// "code_hash": "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63", -// "hash_type": "type", -// "args": "0x2a49720e721553d0614dff29454ee4e1f07d0707" -// }, -// "script_type": "lock", -// "filter": { -// "script_len_range": ["0x0", "0x1"] +// +// // const script = { +// // code_hash: +// // "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", +// // hash_type: "type", +// // args: "0x2760d76d61cafcfc1a83d9d3d6b70c36fa9d4b1a" +// // }; +// export interface ScriptMsg { +// script: ScriptObject +// script_type: "lock" | "type" +// block_number: string +// } +// +// +// export interface ScriptObject { +// code_hash: string; +// hash_type: string | null | undefined; +// args: string | null | undefined; +// } +// +// //{ +// // script, +// // script_type: script_type +// // }, +// // "asc", +// // "0x6400000", +// export interface SearchKey { +// script: ScriptObject, +// script_type: "lock" | "type" +// filter?: SearchFilter +// with_data?: boolean +// } +// +// export interface GetTransactionsSearchKey { +// script: ScriptObject, +// script_type: "lock" | "type" +// filter?: SearchFilter +// group_by_transaction?: boolean +// } +// +// export interface SearchFilter { +// script?: ScriptObject +// script_len_range?: HexString[] +// output_data_len_range?: HexString[] +// output_capacity_range?: HexString[] +// block_range?: HexString[] +// } +// +// export interface GetCellsRequest { +// search_key: SearchKey +// order: "asc" | "desc" +// limit: HexString +// after_cursor?: HexString +// } +// +// export interface GetCellsCapacityReq { +// search_key: SearchKey +// } +// +// /** +// * @description: set_scripts +// * @param {script{code_hash,hash_type,args}} +// * @return {any} +// */ +// +// export async function setScripts(scripts: ScriptMsg[]) { +// const res = await request(1, ckbLightClientRPC, "set_scripts", [ +// scripts +// ]); +// return res; +// } +// +// /** +// * @description: get_tip_header +// * @param {[]} +// * @return {header} +// */ +// export async function getTipHeader(ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "get_tip_header", []); +// return res; +// } +// +// /** +// * @description: get_scripts +// * @return {any} +// * @param ckbLightClient +// */ +// export async function getScripts(ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "get_scripts", []); +// return res; +// } +// +// export async function checkScriptsInLightClient(scripts:ScriptObject[],ckbLightClient:string = ckbLightClientRPC){ +// let res = await getScripts(ckbLightClient) +// // @ts-ignore +// let getScriptList = res.map(result=>{ +// return { +// code_hash:result.script.code_hash, +// hash_type:result.script.hash_type, +// args:result.script.args +// } +// }) +// // @ts-ignore +// return scripts.some(checkScript=>{ +// return getScriptList.some(getScript=>{ +// return getScript.code_hash == checkScript.code_hash && +// getScript.hash_type == checkScript.hash_type && +// getScript.args == checkScript.args +// }) +// }) +// } +// +// export async function waitScriptsUpdate(block_num: BI, ckbLightClient: string = ckbLightClientRPC) { +// while (true) { +// let res = await getScripts(ckbLightClient) +// // @ts-ignore +// if (res.length == 0) { +// return +// } +// // get lowest blocker +// let lower_block_num = BI.from("0xffffffffff") +// // @ts-ignore +// for (let i = 0; i < res.length; i++) { +// if (lower_block_num.gt(res[i].block_number)) { +// lower_block_num = BI.from(res[i].block_number); // } +// } +// console.log('[waitScriptsUpdate] current get script Height:', lower_block_num.toNumber(), ",wait sync height:", block_num.toNumber(),) +// +// if (block_num.lte(lower_block_num)) { +// return +// } +// await sleep(3000) +// } +// } +// +// export async function getCellsCapacityRequest(getCellsReq: GetCellsCapacityReq, ckbLightClient: string = ckbLightClientRPC) { +// +// let request1 = [ +// getCellsReq.search_key]; +// const res = await request(2, ckbLightClient, "get_cells_capacity", request1); +// return { +// block_hash: res.block_hash, +// block_number: res.block_number, +// capacity: res.capacity +// } +// } +// +// export async function getCellsCapacity(script?: ScriptObject, ckbLightClient: string = ckbLightClientRPC, script_type = "lock") { +// +// let get_cells_capacity_request = { +// script: script, +// script_type: script_type, +// // filter?:filter, +// } +// const res = await request(2, ckbLightClient, "get_cells_capacity", [get_cells_capacity_request]); +// console.log('res:', res) +// return res +// } +// +// //[ +// // { +// // "script": { +// // "code_hash": "0x58c5f491aba6d61678b7cf7edf4910b1f5e00ec0cde2f42e0abb4fd9aff25a63", +// // "hash_type": "type", +// // "args": "0x2a49720e721553d0614dff29454ee4e1f07d0707" +// // }, +// // "script_type": "lock", +// // "filter": { +// // "script_len_range": ["0x0", "0x1"] +// // } +// // }, +// // "asc", +// // "0x64" +// // ] +// +// +// export async function getCellsRequest(getCellsReq: GetCellsRequest, ckbLightClient: string = ckbLightClientRPC) { +// +// const infos: Cell[] = []; +// let request1 = [getCellsReq.search_key, getCellsReq.order, getCellsReq.limit]; +// if (getCellsReq.after_cursor != undefined) { +// // @ts-ignore +// request1.push(getCellsReq.after_cursor) +// } +// +// const res = await request(2, ckbLightClient, "get_cells", request1); +// const liveCells = res.objects; +// for (const liveCell of liveCells) { +// const cell: Cell = { +// // @ts-ignore +// cell_output: liveCell.output, +// data: liveCell.output_data, +// out_point: liveCell.out_point, +// block_number: liveCell.block_number +// }; +// // const { ok , push } = DefaultTerminator(index, cell); +// // if (push) { +// // @ts-ignore +// infos.push(cell); +// // } +// } +// +// return { +// objects: infos, +// lastCursor: res.last_cursor +// }; +// } +// +// /** +// * @description: get_cells +// */ +// export async function getCells(script?: ScriptObject, script_type = "lock", ckbLightClient: string = ckbLightClientRPC) { +// const infos: Cell[] = []; +// let get_cells_request = [ +// { +// script, +// script_type: script_type // }, // "asc", -// "0x64" +// "0xfff", // ] - - -export async function getCellsRequest(getCellsReq: GetCellsRequest, ckbLightClient: string = ckbLightClientRPC) { - const infos: Cell[] = []; - let request1 = [ - getCellsReq.search_key, getCellsReq.order, getCellsReq.limit]; - if (getCellsReq.after_cursor != undefined) { - request1.push(getCellsReq.after_cursor) - } - const res = await request(2, ckbLightClient, "get_cells", request1); - const liveCells = res.objects; - for (const liveCell of liveCells) { - const cell: Cell = { - cell_output: liveCell.output, - data: liveCell.output_data, - out_point: liveCell.out_point, - block_number: liveCell.block_number - }; - // const { ok , push } = DefaultTerminator(index, cell); - // if (push) { - infos.push(cell); - // } - } - - return { - objects: infos, - lastCursor: res.last_cursor - }; -} - -/** - * @description: get_cells - */ -export async function getCells(script?: ScriptObject, script_type = "lock", ckbLightClient: string = ckbLightClientRPC) { - const infos: Cell[] = []; - let get_cells_request = [ - { - script, - script_type: script_type - }, - "asc", - "0xfff", - ] - console.log('script:', script) - const res = await request(2, ckbLightClient, "get_cells", get_cells_request); - - const liveCells = res.objects; - let index = 0; - for (const liveCell of liveCells) { - const cell: Cell = { - cell_output: liveCell.output, - data: liveCell.output_data, - out_point: liveCell.out_point, - block_number: liveCell.block_number - }; - // const { ok , push } = DefaultTerminator(index, cell); - // if (push) { - infos.push(cell); - // } - index = index + 1; - } - - return { - objects: infos, - lastCursor: index - }; - // return res; -} - - -/** - * @description: get_transactions - */ - - -export async function getTransactions(searchKey: GetTransactionsSearchKey, searchKeyFilter: SearchKeyFilter = {}, url: string = ckbLightClientRPC, -): Promise { - - let infos: IndexerTransaction[] = []; - let cursor: string | undefined = searchKeyFilter.lastCursor; - const sizeLimit = searchKeyFilter.sizeLimit || 100; - const order = searchKeyFilter.order || "asc"; - while (true) { - const params = [searchKey, order, `0x${sizeLimit.toString(16)}`, cursor]; - const res = await request(2, url, "get_transactions", params); - const txs = res.objects; - cursor = res.last_cursor as string; - infos = infos.concat(txs); - if (txs.length <= sizeLimit) { - break; - } - } - return { - objects: infos, - lastCursor: cursor, - }; -} - -export async function fetch_header(block_hash: string, ckbLightClient: string = ckbLightClientRPC) { - const res = await request(2, ckbLightClient, "fetch_header", [block_hash]); - return res -} - - -export async function fetch_transaction(tx_hash: string, ckbLightClient: string = ckbLightClientRPC) { - const res = await request(2, ckbLightClient, "fetch_transaction", [tx_hash]); - - return res -} - -// const get_cells_capacity_params = [ -// { -// script, -// script_type: "lock" -// } -// ]; - +// console.log('script:', script) +// const res = await request(2, ckbLightClient, "get_cells", get_cells_request); +// +// const liveCells = res.objects; +// let index = 0; +// for (const liveCell of liveCells) { +// const cell: Cell = { +// // @ts-ignore +// cell_output: liveCell.output, +// data: liveCell.output_data, +// out_point: liveCell.out_point, +// block_number: liveCell.block_number +// }; +// // const { ok , push } = DefaultTerminator(index, cell); +// // if (push) { +// infos.push(cell); +// // } +// index = index + 1; +// } +// +// return { +// objects: infos, +// lastCursor: index +// }; +// // return res; +// } +// +// +// /** +// * @description: get_transactions +// */ +// +// +// export async function getTransactions(searchKey: GetTransactionsSearchKey, searchKeyFilter: SearchKeyFilter = {}, url: string = ckbLightClientRPC, +// ): Promise { +// +// let infos: IndexerTransaction[] = []; +// let cursor: string | undefined = searchKeyFilter.lastCursor; +// const sizeLimit = searchKeyFilter.sizeLimit || 100; +// const order = searchKeyFilter.order || "asc"; +// while (true) { +// const params = [searchKey, order, `0x${sizeLimit.toString(16)}`, cursor]; +// const res = await request(2, url, "get_transactions", params); +// const txs = res.objects; +// cursor = res.last_cursor as string; +// infos = infos.concat(txs); +// if (txs.length <= sizeLimit) { +// break; +// } +// } +// return { +// objects: infos, +// lastCursor: cursor, +// }; +// } +// +// +// +// export async function fetch_transaction(tx_hash: string, ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(2, ckbLightClient, "fetch_transaction", [tx_hash]); +// +// return res +// } +// +// // const get_cells_capacity_params = [ +// // { +// // script, +// // script_type: "lock" +// // } +// // ]; +// +// // /** +// // * @description: get_cells_capacity +// // */ +// // export async function getCellsCapacity() { +// // const res = await request( +// // 2, +// // ckbLightClientRPC, +// // "get_cells_capacity", +// // get_cells_capacity_params +// // ); +// // return res; +// // } +// +// /** +// * @description: get_transaction +// */ +// export async function getTransaction(hash: string, ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "get_transaction", [hash]); +// return res; +// } +// +// +// async function sleep(timeOut: number) { +// await new Promise(r => setTimeout(r, timeOut)); +// } +// +// +// /** +// * @description: get_header +// */ +// export async function getHeader(hash: string, ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "get_header", [hash]); +// return res; +// } +// // /** -// * @description: get_cells_capacity +// * @description: get_peers // */ -// export async function getCellsCapacity() { -// const res = await request( -// 2, -// ckbLightClientRPC, -// "get_cells_capacity", -// get_cells_capacity_params -// ); -// return res; +// export async function getPeers(ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "get_peers", []); +// return res; +// } +// +// /** +// * @description: send_transaction +// */ +// export async function sendTransaction(tx: any, ckbLightClient: string = ckbLightClientRPC) { +// const res = await request(1, ckbLightClient, "send_transaction", [tx]); +// return res; +// } +// +// export async function getGenesisBlock(ckbLightClient: string = ckbLightClientRPC) { +// return await request(1, ckbLightClient, "get_genesis_block", []) // } - -/** - * @description: get_transaction - */ -export async function getTransaction(hash: string, ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "get_transaction", [hash]); - return res; -} - - -async function sleep(timeOut: number) { - await new Promise(r => setTimeout(r, timeOut)); -} - - -/** - * @description: get_header - */ -export async function getHeader(hash: string, ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "get_header", [hash]); - return res; -} - -/** - * @description: get_peers - */ -export async function getPeers(ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "get_peers", []); - return res; -} - -/** - * @description: send_transaction - */ -export async function sendTransaction(tx: any, ckbLightClient: string = ckbLightClientRPC) { - const res = await request(1, ckbLightClient, "send_transaction", [tx]); - return res; -} - -export async function getGenesisBlock(ckbLightClient: string = ckbLightClientRPC) { - return await request(1, ckbLightClient, "get_genesis_block", []) -} diff --git a/service/CkbDevService.ts b/service/CkbDevService.ts new file mode 100644 index 0000000..0179aea --- /dev/null +++ b/service/CkbDevService.ts @@ -0,0 +1,179 @@ +import { + ACCOUNT_PRIVATE, CKB_DEV_INDEX_PATH, CKB_DEV_LIGHT_CLIENT_PATH, CKB_DEV_PATH, + CKB_DEV_RPC_INDEX_URL, + CKB_DEV_RPC_URL, + CKB_LIGHT_RPC_URL, DEV_PATH, lightClientRPC, + rpcDevCLient +} from "../config/config"; +import {BI} from "@ckb-lumos/bi"; +import {request} from "./index"; +import {getCellsByRange} from "./txService"; +import {generateAccountFromPrivateKey, TransferService} from "./transfer"; +import {sh, shWithTimeout, shWithTimeOutNotErr} from "./node"; +import {Sleep} from "./util"; +import {getCellsCapacityRequest, waitScriptsUpdate} from "./lightService"; +import {Script} from "@ckb-lumos/base"; + +export const transferDevService = new TransferService(CKB_DEV_RPC_URL,CKB_DEV_RPC_INDEX_URL) + +export async function cut_miner_and_wait_lightClient_sync(cut_num: number, miner_num: number) { + const before_cut_tip_height = await rpcDevCLient.getTipBlockNumber() + if (BI.from(before_cut_tip_height).toNumber() < cut_num) { + await miner_block_number(cut_num - BI.from(before_cut_tip_height).toNumber()) + } + await cut_number(cut_num) + await miner_block_number(miner_num) + let tip_num = await rpcDevCLient.getTipBlockNumber() + await waitScriptsUpdate(BI.from(tip_num)) +} + +export async function compare_cells_result(scriptObject1: Script) { + let compare = true; + const indexCells = await getCellsMsg(scriptObject1, CKB_DEV_RPC_INDEX_URL) + console.log("lightCells:") + const lightCells = await getCellsMsg(scriptObject1, CKB_LIGHT_RPC_URL) + // get indexCells but not in light + const indexNotInLightCells = indexCells.filter(cell => !lightCells.some(lightCell => { + return lightCell.blockNumber == cell.blockNumber && + lightCell.outPoint?.txHash == cell.outPoint?.txHash + })) + + // get lightCells but not in index + const lightNotInIndexCells = lightCells.filter(cell => !indexCells.some(indexCell => { + return indexCell.blockNumber == cell.blockNumber && + indexCell.outPoint?.txHash == cell.outPoint?.txHash + })) + if (indexNotInLightCells.length != 0) { + + compare = false + console.log("indexNotInLightCells") + indexNotInLightCells.forEach(cell => { + console.log( + "blockNum:", BI.from(cell.blockNumber).toNumber(), + " hash:", cell.outPoint?.txHash, + " index:", cell.outPoint?.index + ) + }) + + } + if (lightNotInIndexCells.length != 0) { + compare = false + console.log("lightNotInIndexCells") + lightNotInIndexCells.forEach(cell => { + console.log( + "blockNum:", BI.from(cell.blockNumber).toNumber(), + " hash:", cell.outPoint?.txHash, + " index:", cell.outPoint?.index + ) + }) + } + return compare +} + +export async function cut_number(cut_number: number) { + const tip_number = BI.from(await rpcDevCLient.getTipBlockNumber()).toNumber() + const reset_num = tip_number - cut_number + return truncate_to_block(reset_num) +} +export async function truncate_to_block(block_number:number){ + const hash = await rpcDevCLient.getBlockHash(BI.from(block_number).toHexString()) + await truncate(CKB_DEV_RPC_URL, hash) +} + +export async function truncate(url, hash: string) { + await request(100, url, "truncate", [hash]) +} + +export async function miner_block_until_number(end_number: number) { + + for (let i = 0; i < 10000; i++) { + await miner_block() + const tip_number = BI.from(await rpcDevCLient.getTipBlockNumber()).toNumber() + if (tip_number > end_number) { + return + } + console.log("[miner]current:" + tip_number + ", expected:", end_number) + } + +} + +export async function miner_block_number(height: number) { + const begin_number = BI.from(await rpcDevCLient.getTipBlockNumber()).toNumber() + const end_number = begin_number + height + await miner_block_until_number(end_number) +} + +export async function getCellsMsg(scriptObject1: Script, url: string) { + const tip_number = await rpcDevCLient.getTipBlockNumber(); + let cells = await getCellsByRange(scriptObject1, "lock", undefined, url, ["0x0", BI.from(tip_number).toHexString()]) + cells.forEach(tx => console.log( + "blockNum:", BI.from(tx.blockNumber).toNumber(), + " hash:", tx.outPoint?.txHash, + " index:", tx.outPoint?.index + )) + return cells + +} + + + +export async function miner_block(kill_port: boolean = true) { + if (kill_port) { + await shWithTimeOutNotErr("lsof -i:8888 | grep LIS | awk '{print $2}'| xargs -n1 kill -9", 2000) + } + await shWithTimeOutNotErr(" cd " + CKB_DEV_PATH + " && ./ckb miner -C dev -l 40 > block.log", 2000) + if(kill_port){ + await shWithTimeOutNotErr("lsof -i:8888 | grep LIS | awk '{print $2}'| xargs -n1 kill -9", 2000) + } + await shWithTimeOutNotErr("cat " + CKB_DEV_PATH + "/block.log | grep Found", 1000) +} + +export async function cleanCkbLightClientEnv() { + await sh("cd " + DEV_PATH + " && rm -rf ckb-light-client/target/release/data") +} + +export async function stopCkbLightClient() { + await shWithTimeOutNotErr("pkill ckb-light-client",1000) +} + +export async function startCkbLightClient() { + await sh("cd " + CKB_DEV_LIGHT_CLIENT_PATH +" && RUST_LOG=info,ckb_light_client=trace ./ckb-light-client run --config-file ./config.toml > node.log 2>&1 &") +} + +export async function cleanAndRestartCkbLightClientEnv() { + await stopCkbLightClient() + await cleanCkbLightClientEnv() + await startCkbLightClient() +} + +export async function cleanAllEnv() { + await sh("cd " + DEV_PATH + " && sh clean.sh") +} + +export async function startEnv() { + await shWithTimeout("cd " + DEV_PATH + " sh start.sh", 100_000) +} + +export async function restartAndSyncCkbIndex() { + await shWithTimeOutNotErr(" pkill ckb-indexer",1000) + await sh("cd " + CKB_DEV_INDEX_PATH + " && rm -rf ckb-test && RUST_LOG=info ./ckb-indexer -s ckb-test > ckb-indexer.log 2>&1 &") + await checkCKbIndexSync() +} + +export async function checkCKbIndexSync() { + let acc1 = generateAccountFromPrivateKey(ACCOUNT_PRIVATE); + let tip_num = await rpcDevCLient.getTipBlockNumber() + for (let i = 0; i < 1000; i++) { + let cap = await getCellsCapacityRequest( + { + script: acc1.lockScript, + scriptType: "lock" + + },CKB_DEV_RPC_INDEX_URL) + if (BI.from(tip_num).lte(BI.from(cap.blockNumber))) { + return + } + console.log("current sync:", BI.from(cap.blockNumber).toNumber(), " expected:", BI.from(tip_num).toNumber()) + await Sleep(1000) + } +} diff --git a/service/deploy.util.ts b/service/deploy.util.ts index f82b91c..f1d7c18 100644 --- a/service/deploy.util.ts +++ b/service/deploy.util.ts @@ -57,7 +57,7 @@ export async function upgradeContract(privateKey:string, SCRIPTBINARY: Uint8Arra const message = txSkeleton.get("signingEntries").get(0)?.message; const Sig = hd.key.signRecoverable(message!, ACCOUNT_PRIVATE); let tx1 = helpers.sealTransaction(txSkeleton, [Sig]); - let tx = await rpc.send_transaction(tx1, "passthrough"); + let tx = await rpc.sendTransaction(tx1, "passthrough"); console.log('tx:',tx) return deployResult.scriptConfig @@ -106,7 +106,7 @@ export async function deployContractByArray(privateKey: string, SCRIPTBINARY: Ui const message = txSkeleton.get("signingEntries").get(0)?.message; const Sig = hd.key.signRecoverable(message!, ACCOUNT_PRIVATE); let tx1 = helpers.sealTransaction(txSkeleton, [Sig]); - let tx = await rpc.send_transaction(tx1, "passthrough"); + let tx = await rpc.sendTransaction(tx1, "passthrough"); console.log('tx:',tx) return deployResult.scriptConfig } @@ -155,8 +155,8 @@ export async function deployContractByArray(privateKey: string, SCRIPTBINARY: Ui function subFee(txSkeleton: TransactionSkeletonType): TransactionSkeletonType { txSkeleton.get("outputs").map(function (outPut, idx) { - if (outPut.cell_output.type == null) { - outPut.cell_output.capacity = BI.from(outPut.cell_output.capacity).sub(BI.from(FEE)).toHexString() + if (outPut.cellOutput.type == null) { + outPut.cellOutput.capacity = BI.from(outPut.cellOutput.capacity).sub(BI.from(FEE)).toHexString() txSkeleton.get("outputs").set(idx, outPut) return txSkeleton } diff --git a/service/index.ts b/service/index.ts index dd2330c..323de73 100644 --- a/service/index.ts +++ b/service/index.ts @@ -1,79 +1,13 @@ import {utils, Script, ScriptWrapper, HexString} from "@ckb-lumos/base"; -import { - CKBIndexerQueryOptions, - HexadecimalRange, - SearchFilter, - ScriptType, - SearchKey -} from "./type"; import fetch from "cross-fetch"; -import {BI} from "@ckb-lumos/bi"; -import {CKB_RPC_URL, RPC_DEBUG} from "../config/config"; -// import {RPC_DEBUG} from "../config/config" -// eslint-disable-next-line no-unused-vars +import { RPC_DEBUG} from "../config/config"; const RPC_DEBUG_SERVICE = RPC_DEBUG function instanceOfScriptWrapper(object: unknown): object is ScriptWrapper { return typeof object === "object" && object != null && "script" in object; } -const UnwrapScriptWrapper = (inputScript: ScriptWrapper | Script): Script => { - if (instanceOfScriptWrapper(inputScript)) { - return inputScript.script; - } - return inputScript; -}; -const generateSearchKey = (queries: CKBIndexerQueryOptions): SearchKey => { - let script: Script | undefined = undefined; - const filter: SearchFilter = {}; - let script_type: ScriptType | undefined = undefined; - if (queries.lock) { - const lock = UnwrapScriptWrapper(queries.lock); - script = lock as Script; - script_type = "lock"; - if (queries.type && typeof queries.type !== "string") { - const type = UnwrapScriptWrapper(queries.type); - filter.script = type as Script; - } - } else if (queries.type && typeof queries.type !== "string") { - const type = UnwrapScriptWrapper(queries.type); - script = type as Script; - script_type = "type"; - } - let block_range: HexadecimalRange | null = null; - if (queries.fromBlock && queries.toBlock) { - //toBlock+1 cause toBlock need to be included - block_range = [ - queries.fromBlock, - `0x${BI.from(queries.toBlock).add(1).toString(16)}` - ]; - } - if (block_range) { - filter.block_range = block_range; - } - if (queries.outputDataLenRange) { - filter.output_data_len_range = queries.outputDataLenRange; - } - if (queries.outputCapacityRange) { - filter.output_capacity_range = queries.outputCapacityRange; - } - if (!script) { - throw new Error("Either lock or type script must be provided!"); - } - if (!script_type) { - throw new Error("script_type must be provided"); - } - return { - script, - script_type, - filter - }; -}; -const getHexStringBytes = (hexString: HexString) => { - utils.assertHexString("", hexString); - return Math.ceil(hexString.substr(2).length / 2); -}; const requestBatch = async (rpcUrl: string, data: unknown): Promise => { const res: Response = await fetch(rpcUrl, { @@ -161,8 +95,6 @@ const request = async ( }; export { - generateSearchKey, - getHexStringBytes, instanceOfScriptWrapper, requestBatch, request, diff --git a/service/lightService.ts b/service/lightService.ts new file mode 100644 index 0000000..a65299d --- /dev/null +++ b/service/lightService.ts @@ -0,0 +1,122 @@ +import {BI} from "@ckb-lumos/bi"; +import {CKB_LIGHT_RPC_URL, CKB_RPC_URL, lightClientRPC} from "../config/config"; +import {LightClientRPC} from "@ckb-lumos/light-client"; +import {Sleep} from "./util"; +import {request} from "./index"; +import {SearchKey} from "@ckb-lumos/ckb-indexer/src/type"; +import {blockchain, HexString, Script} from "@ckb-lumos/base"; +import {Cell} from "@ckb-lumos/base/lib/api"; +import {GetCellsSearchKey} from "@ckb-lumos/ckb-indexer/lib/type"; +import { + toGetCellsSearchKey, +} from "@ckb-lumos/ckb-indexer/lib/paramsFormatter"; +import {utils} from "@ckb-lumos/base"; +import {deepCamelizeTransaction} from "@ckb-lumos/base/lib/utils"; +import {RPC} from "@ckb-lumos/lumos"; + +const ckbLightClientRPC = CKB_LIGHT_RPC_URL; + +export interface GetCellsRequest { + searchKey: GetCellsSearchKey + order: "asc" | "desc" + limit: HexString + afterCursor?: HexString +} + +export async function getGenesisBlock(ckbLightClient: string = ckbLightClientRPC) { + return await request(1, ckbLightClient, "get_genesis_block", []) +} + +export async function getHeader(hash: string, ckbLightClient: string = ckbLightClientRPC) { + const res = await request(1, ckbLightClient, "get_header", [hash]); + return res; +} +export async function getTransaction(hash:string,ckbLightClient = ckbLightClientRPC){ + return await request(1, ckbLightClient, "get_transaction", [hash]) + +} + +export async function getCellsCapacityRequest(getCellsReq: SearchKey, ckbLightClient: string = ckbLightClientRPC) { + + let request1 = [toGetCellsSearchKey(getCellsReq)]; + const res = await request(2, ckbLightClient, "get_cells_capacity", request1); + return { + blockHash: res.block_hash, + blockNumber: res.block_number, + capacity: res.capacity + } +} + +export async function sendTransaction(tx: any, ckbLightClient: string = ckbLightClientRPC) { + const client = new RPC(ckbLightClient); + const res = await client.sendTransaction(tx); + return res; +} + + +export async function waitScriptsUpdate(block_num: BI, ckbLightClient: string = ckbLightClientRPC) { + const lightClientRPC = new LightClientRPC(ckbLightClient) + + while (true) { + let res = await lightClientRPC.getScripts() + // @ts-ignore + if (res.length == 0) { + return + } + // get lowest blocker + let lower_block_num = BI.from("0xffffffffff") + // @ts-ignore + for (let i = 0; i < res.length; i++) { + if (lower_block_num.gt(res[i].blockNumber)) { + lower_block_num = BI.from(res[i].blockNumber); + } + } + console.log('[waitScriptsUpdate] current get script Height:', lower_block_num.toNumber(), ",wait sync height:", block_num.toNumber(),) + + if (block_num.lte(lower_block_num)) { + return + } + await Sleep(3000) + } +} + + +export async function checkScriptsInLightClient(scripts:Script[],ckbLightClient:string = ckbLightClientRPC){ + let res = await lightClientRPC.getScripts() + + let getScriptList = res.map(result=>{ + return { + codeHash:result.script.codeHash, + hashType:result.script.hashType, + args:result.script.args + } + }) + + return scripts.some(checkScript=>{ + return getScriptList.some(getScript=>{ + return getScript.codeHash == checkScript.codeHash && + getScript.hashType == checkScript.hashType && + getScript.args == checkScript.args + }) + }) +} + +export async function getCellsRequest(getCellsReq: GetCellsRequest, ckbLightClient: string = ckbLightClientRPC) { + const lightRpc = new LightClientRPC(ckbLightClient) + const infos: Cell[] = []; + let res = await lightRpc.getCells(getCellsReq.searchKey,getCellsReq.order,getCellsReq.limit,getCellsReq.afterCursor) + const liveCells = res.objects; + for (const liveCell of liveCells) { + const cell: Cell = { + cellOutput: liveCell.output, + data: liveCell.outputData, + outPoint: liveCell.outPoint, + blockNumber: liveCell.blockNumber + }; + infos.push(cell); + } + return { + objects: infos, + lastCursor: res.lastCursor + }; +} diff --git a/service/node.ts b/service/node.ts index 9f7ba4f..0524ebf 100644 --- a/service/node.ts +++ b/service/node.ts @@ -51,11 +51,33 @@ export { } -async function sh(cmd: string) { +export async function shWithTimeOutNotErr(cmd:string,timeout:number){ console.log('sh:', cmd) return new Promise(function (resolve, reject) { - exec(cmd, { timeout: 10000},(err, stdout, stderr) => { + let c = exec(cmd, { timeout: timeout},(err, stdout, stderr) => { if (err) { + // console.log(err) + if(!c.killed){ + c.kill() + } + resolve(err); + } else { + console.log('response:', stdout) + resolve({stdout, stderr}); + } + }); + }); +} +export async function shWithTimeout(cmd:string,timeout:number){ + console.log('sh:', cmd) + return new Promise(function (resolve, reject) { + let c = exec(cmd, { timeout: timeout},(err, stdout, stderr) => { + if (err) { + console.log(err) + console.log(c.pid) + console.log("killed:",c.killed) + // c.kill() + console.log("killed:",c.killed) reject(err); } else { console.log('response:', stdout) @@ -63,6 +85,9 @@ async function sh(cmd: string) { } }); }); +} +export async function sh(cmd: string) { + return await shWithTimeout(cmd,10000) } diff --git a/service/sudt.ts b/service/sudt.ts index fa7fb53..a74660e 100644 --- a/service/sudt.ts +++ b/service/sudt.ts @@ -10,26 +10,27 @@ import {Cell, HashType} from "@ckb-lumos/base/lib/api"; -import {BI, helpers} from "@ckb-lumos/lumos"; +import { helpers} from "@ckb-lumos/lumos"; import {AGGRON4} from "./transfer"; import {utils} from "@ckb-lumos/base"; -import {BIish} from "@ckb-lumos/bi"; +import {BI,BIish} from "@ckb-lumos/bi"; +import{ number, bytes } from "@ckb-lumos/codec" export function issueTokenCell(from: string,amount :BIish):Cell { const toScript = helpers.parseAddress(from, {config: AGGRON4}); return { - cell_output: { - capacity: BI.from(240).mul(100000000).toHexString(), + cellOutput: { + capacity: BI.from(150).mul(100000000).toHexString(), lock: toScript, type:{ - code_hash: AGGRON4.SCRIPTS.SUDT.CODE_HASH, - hash_type: AGGRON4.SCRIPTS.SUDT.HASH_TYPE, + codeHash: AGGRON4.SCRIPTS.SUDT.CODE_HASH, + hashType: AGGRON4.SCRIPTS.SUDT.HASH_TYPE, args: utils.computeScriptHash(toScript) } }, - data: utils.toBigUInt128LE(amount), + data:bytes.hexify(number.Uint128LE.pack(amount)), }; } diff --git a/service/sudt.util.ts b/service/sudt.util.ts index 3bde6a7..6b63610 100644 --- a/service/sudt.util.ts +++ b/service/sudt.util.ts @@ -1,10 +1,9 @@ import {BIish} from "@ckb-lumos/bi"; -import {BI, commons, hd, helpers, Indexer, RPC} from "@ckb-lumos/lumos"; +import { commons, hd, helpers, Indexer, RPC} from "@ckb-lumos/lumos"; import {ACCOUNT_PRIVATE, CKB_RPC_INDEX_URL, CKB_RPC_URL, FEE, FeeRate} from "../config/config"; import {AGGRON4, generateAccountFromPrivateKey} from "./transfer"; import {sudt} from "@ckb-lumos/common-scripts"; import {utils} from "@ckb-lumos/base"; -import {TransactionSkeletonType} from "@ckb-lumos/helpers"; const ckbRpcUrl = CKB_RPC_URL; const ckbRpcIndexUrl = CKB_RPC_INDEX_URL; @@ -33,7 +32,7 @@ export async function issueToken(privateKey: string, amount: BIish): Promise { + const collector = this.defaultIndexer.collector({ + lock: helpers.parseAddress(address, {config: AGGRON4}), + }); + + let balance = BI.from(0); + for await (const cell of collector.collect()) { + balance = balance.add(cell.cellOutput.capacity); + } + return balance; + } + + async transfer(options: transferOptions): Promise { + const transferOutput: Cell = getOutPutCell(options.to, options.amount, "0x"); + return this.send_tx({ + from: options.from, + outputCells: [transferOutput], + privKey: options.privKey, + }); + } + + async send_tx(options: Options): Promise { + let txSkeleton = helpers.TransactionSkeleton({}); + const fromScript = helpers.parseAddress(options.from, {config: AGGRON4}); + + let neededCapacity = BI.from(0) + for (let i = 0; i < options.outputCells.length; i++) { + neededCapacity = neededCapacity.add(options.outputCells[i].cellOutput.capacity) + } + let collectedSum = BI.from(0); + const collected: Cell[] = []; + const collector = this.defaultIndexer.collector({lock: fromScript, type: "empty"}); + for await (const cell of collector.collect()) { + collectedSum = collectedSum.add(cell.cellOutput.capacity); + collected.push(cell); + if (collectedSum >= neededCapacity) break; + } + console.log('total cell balance: ', collectedSum.toString()) + + if (collectedSum < neededCapacity) { + throw new Error("Not enough CKB"); + } + + if (collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).gt(BI.from('0'))) { + const changeOutput: Cell = { + cellOutput: { + capacity: collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).toHexString(), + lock: fromScript, + }, + data: "0x", + }; + console.log('gen out put extra value ') + txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(changeOutput)); + } + let SECP256K1_BLAKE160_HASH = (await this.defaultRpc.getBlockByNumber("0x0")).transactions[1].hash + console.log("SECP256K1_BLAKE160_HASH:", SECP256K1_BLAKE160_HASH) + txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(...options.outputCells)); + txSkeleton = txSkeleton.update("inputs", (inputs) => inputs.push(...collected)); + txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => + cellDeps.push(...[ + { + outPoint: { + txHash: SECP256K1_BLAKE160_HASH, + index: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.INDEX, + }, + depType: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, + } + ]) + ); + txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => cellDeps.push(...options.deps)); + + const firstIndex = txSkeleton + .get("inputs") + .findIndex((input) => + new ScriptValue(input.cellOutput.lock, {validate: false}).equals( + new ScriptValue(fromScript, {validate: false}) + ) + ); + if (firstIndex !== -1) { + while (firstIndex >= txSkeleton.get("witnesses").size) { + txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.push("0x")); + } + let witness: string = txSkeleton.get("witnesses").get(firstIndex)!; + const newWitnessArgs: WitnessArgs = { + /* 65-byte zeros in hex */ + lock: + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }; + if (witness !== "0x") { + const witnessArgs = blockchain.WitnessArgs.unpack(bytes.bytify(witness)) + const lock = witnessArgs.lock; + if (!!lock && lock !== newWitnessArgs.lock) { + throw new Error("Lock field in first witness is set aside for signature!"); + } + const inputType = witnessArgs.inputType; + if (!!inputType) { + newWitnessArgs.inputType = inputType; + } + const outputType = witnessArgs.outputType; + if (!!outputType) { + newWitnessArgs.outputType = outputType; + } + } + witness = bytes.hexify(blockchain.WitnessArgs.pack(newWitnessArgs)) + + txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(firstIndex, witness)); + } + + + txSkeleton = commons.common.prepareSigningEntries(txSkeleton); + const message = txSkeleton.get("signingEntries").get(0)?.message; + const Sig = hd.key.signRecoverable(message!, options.privKey); + const tx = helpers.sealTransaction(txSkeleton, [Sig]); + + if (options.lightMode == true) { + if (options.lightNotInstallCellMode == null || options.lightNotInstallCellMode == false) { + await installTxCells(tx) + } + + const hash = await new RPC(CKB_LIGHT_RPC_URL).sendTransaction(tx) + console.log("The CKB_LIGHT_RPC_URL transaction hash is", hash); + return hash; + } + const hash = await this.defaultRpc.sendTransaction(tx, "passthrough"); + console.log("The transaction hash is", hash); + return hash; + } +} type Account = { lockScript: Script; @@ -46,11 +187,11 @@ export const generateAccountFromPrivateKey = (privKey: string): Account => { const args = hd.key.publicKeyToBlake160(pubKey); const template = AGGRON4.SCRIPTS.SECP256K1_BLAKE160!; const lockScript = { - code_hash: template.CODE_HASH, - hash_type: template.HASH_TYPE, + codeHash: template.CODE_HASH, + hashType: template.HASH_TYPE, args: args, }; - const address = helpers.generateAddress(lockScript, { config: AGGRON4 }); + const address = helpers.generateAddress(lockScript, {config: AGGRON4}); return { lockScript, address, @@ -59,13 +200,13 @@ export const generateAccountFromPrivateKey = (privKey: string): Account => { }; export async function capacityOf(address: string): Promise { - const collector = indexer.collector({ - lock: helpers.parseAddress(address, { config: AGGRON4 }), + const collector = defaultIndexer.collector({ + lock: helpers.parseAddress(address, {config: AGGRON4}), }); let balance = BI.from(0); for await (const cell of collector.collect()) { - balance = balance.add(cell.cell_output.capacity); + balance = balance.add(cell.cellOutput.capacity); } return balance; @@ -77,21 +218,22 @@ interface transferOptions { amount: string; privKey: string; } + interface Options { from: string; outputCells: Cell[]; privKey: string; inputCells?: Cell[]; - deps?:CellDep[]; - lightMode?:boolean; - lightNotInstallCellMode?:boolean; + deps?: CellDep[]; + lightMode?: boolean; + lightNotInstallCellMode?: boolean; } -function getOutPutCell(to:string,amount: string,data:string):Cell{ +function getOutPutCell(to: string, amount: string, data: string): Cell { - const toScript = helpers.parseAddress(to, { config: AGGRON4 }); + const toScript = helpers.parseAddress(to, {config: AGGRON4}); return { - cell_output: { + cellOutput: { capacity: BI.from(amount).mul(100000000).toHexString(), lock: toScript, }, @@ -99,20 +241,21 @@ function getOutPutCell(to:string,amount: string,data:string):Cell{ }; } + export async function transfer(options: transferOptions): Promise { - const transferOutput: Cell = getOutPutCell(options.to,options.amount,"0x"); + const transferOutput: Cell = getOutPutCell(options.to, options.amount, "0x"); return send_tx({ - from:options.from, - outputCells:[transferOutput], - privKey:options.privKey, + from: options.from, + outputCells: [transferOutput], + privKey: options.privKey, }); } -export async function send_tx_with_input(options:Options):Promise{ +export async function send_tx_with_input(options: Options): Promise { let txSkeleton = helpers.TransactionSkeleton({}); - const fromScript = helpers.parseAddress(options.from, { config: AGGRON4 }); + const fromScript = helpers.parseAddress(options.from, {config: AGGRON4}); txSkeleton = txSkeleton.update("inputs", (inputs) => inputs.push(...options.inputCells)); @@ -121,44 +264,34 @@ export async function send_tx_with_input(options:Options):Promise{ cellDeps.push(...[ { - out_point: { - tx_hash: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, + outPoint: { + txHash: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, index: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, - },{ - out_point: { - tx_hash: AGGRON4.SCRIPTS.SUDT.TX_HASH, + depType: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, + }, { + outPoint: { + txHash: AGGRON4.SCRIPTS.SUDT.TX_HASH, index: AGGRON4.SCRIPTS.SUDT.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, + depType: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, }, { - out_point: { - tx_hash: EVERY_ONE_CAN_PAY.TX_HASH, + outPoint: { + txHash: EVERY_ONE_CAN_PAY.TX_HASH, index: EVERY_ONE_CAN_PAY.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, + depType: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, } - // { - // out_point: { - // tx_hash: ANY_ONE_CAN_PAY.TX_HASH, - // index: ANY_ONE_CAN_PAY.INDEX, - // }, - // dep_type: "code", - // } - ]) + ]) ); - // txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => - // cellDeps.push() - // ); const firstIndex = txSkeleton .get("inputs") .findIndex((input) => - new ScriptValue(input.cell_output.lock, { validate: false }).equals( - new ScriptValue(fromScript, { validate: false }) + new ScriptValue(input.cellOutput.lock, {validate: false}).equals( + new ScriptValue(fromScript, {validate: false}) ) ); if (firstIndex !== -1) { @@ -172,23 +305,21 @@ export async function send_tx_with_input(options:Options):Promise{ "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }; if (witness !== "0x") { - const witnessArgs = new core.WitnessArgs(new toolkit.Reader(witness)); - const lock = witnessArgs.getLock(); - if (lock.hasValue() && new toolkit.Reader(lock.value().raw()).serializeJson() !== newWitnessArgs.lock) { + const witnessArgs = blockchain.WitnessArgs.unpack(bytes.bytify(witness)) + const lock = witnessArgs.lock; + if (!!lock && lock !== newWitnessArgs.lock) { throw new Error("Lock field in first witness is set aside for signature!"); } - const inputType = witnessArgs.getInputType(); - if (inputType.hasValue()) { - newWitnessArgs.input_type = new toolkit.Reader(inputType.value().raw()).serializeJson(); + const inputType = witnessArgs.inputType; + if (!!inputType) { + newWitnessArgs.inputType = inputType; } - const outputType = witnessArgs.getOutputType(); - if (outputType.hasValue()) { - newWitnessArgs.output_type = new toolkit.Reader(outputType.value().raw()).serializeJson(); + const outputType = witnessArgs.outputType; + if (!!outputType) { + newWitnessArgs.outputType = outputType; } } - witness = new toolkit.Reader( - core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs)) - ).serializeJson(); + witness = bytes.hexify(blockchain.WitnessArgs.pack(newWitnessArgs)) txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(firstIndex, witness)); } @@ -196,32 +327,32 @@ export async function send_tx_with_input(options:Options):Promise{ const message = txSkeleton.get("signingEntries").get(0)?.message; const Sig = hd.key.signRecoverable(message!, options.privKey); const tx = helpers.sealTransaction(txSkeleton, [Sig]); - const hash = await defaultRpc.send_transaction(tx, "passthrough"); + const hash = await defaultRpc.sendTransaction(tx, "passthrough"); console.log("The transaction hash is", hash); return hash; } -export async function send_tx_options(options:Options):Promise{ +export async function send_tx_options(options: Options): Promise { let txSkeleton = helpers.TransactionSkeleton({}); - const fromScript = helpers.parseAddress(options.from, { config: AGGRON4 }); + const fromScript = helpers.parseAddress(options.from, {config: AGGRON4}); const neededCap = options.outputCells.reduce((total, cell) => { - return total.add(BI.from(cell.cell_output.capacity)) + return total.add(BI.from(cell.cellOutput.capacity)) }, BI.from(0)) const inputCap = options.inputCells.reduce((total, cell) => { - return total.add(BI.from(cell.cell_output.capacity)) + return total.add(BI.from(cell.cellOutput.capacity)) }, BI.from(0)) - if (inputCap.lt( neededCap.toNumber())) { - throw new Error("Not enough CKB"+"input:"+inputCap.toNumber()+",output:"+neededCap.toNumber()); + if (inputCap.lt(neededCap.toNumber())) { + throw new Error("Not enough CKB" + "input:" + inputCap.toNumber() + ",output:" + neededCap.toNumber()); } - if(inputCap.sub(neededCap).sub(FeeRate.NORMAL).gt(BI.from('0'))) { + if (inputCap.sub(neededCap).sub(FeeRate.NORMAL).gt(BI.from('0'))) { const changeOutput: Cell = { - cell_output: { + cellOutput: { capacity: inputCap.sub(neededCap).sub(FeeRate.NORMAL).toHexString(), lock: fromScript, }, @@ -236,34 +367,34 @@ export async function send_tx_options(options:Options):Promise{ txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => cellDeps.push(...[ { - out_point: { - tx_hash: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, + outPoint: { + txHash: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, index: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, - },{ - out_point: { - tx_hash: AGGRON4.SCRIPTS.SUDT.TX_HASH, + depType: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, + }, { + outPoint: { + txHash: AGGRON4.SCRIPTS.SUDT.TX_HASH, index: AGGRON4.SCRIPTS.SUDT.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, + depType: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, }, { - out_point: { - tx_hash: EVERY_ONE_CAN_PAY_TYPE_ID.TX_HASH, + outPoint: { + txHash: EVERY_ONE_CAN_PAY_TYPE_ID.TX_HASH, index: EVERY_ONE_CAN_PAY_TYPE_ID.INDEX, }, - dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, + depType: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, } ]) ); - txSkeleton = txSkeleton.update("cellDeps",(cellDeps)=> cellDeps.push(...options.deps)); + txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => cellDeps.push(...options.deps)); const firstIndex = txSkeleton .get("inputs") .findIndex((input) => - new ScriptValue(input.cell_output.lock, { validate: false }).equals( - new ScriptValue(fromScript, { validate: false }) + new ScriptValue(input.cellOutput.lock, {validate: false}).equals( + new ScriptValue(fromScript, {validate: false}) ) ); if (firstIndex !== -1) { @@ -277,23 +408,21 @@ export async function send_tx_options(options:Options):Promise{ "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }; if (witness !== "0x") { - const witnessArgs = new core.WitnessArgs(new toolkit.Reader(witness)); - const lock = witnessArgs.getLock(); - if (lock.hasValue() && new toolkit.Reader(lock.value().raw()).serializeJson() !== newWitnessArgs.lock) { + const witnessArgs = blockchain.WitnessArgs.unpack(bytes.bytify(witness)) + const lock = witnessArgs.lock; + if (!!lock && lock !== newWitnessArgs.lock) { throw new Error("Lock field in first witness is set aside for signature!"); } - const inputType = witnessArgs.getInputType(); - if (inputType.hasValue()) { - newWitnessArgs.input_type = new toolkit.Reader(inputType.value().raw()).serializeJson(); + const inputType = witnessArgs.inputType; + if (!!inputType) { + newWitnessArgs.inputType = inputType; } - const outputType = witnessArgs.getOutputType(); - if (outputType.hasValue()) { - newWitnessArgs.output_type = new toolkit.Reader(outputType.value().raw()).serializeJson(); + const outputType = witnessArgs.outputType; + if (!!outputType) { + newWitnessArgs.outputType = outputType; } } - witness = new toolkit.Reader( - core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs)) - ).serializeJson(); + witness = bytes.hexify(blockchain.WitnessArgs.pack(newWitnessArgs)) txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(firstIndex, witness)); } @@ -302,7 +431,7 @@ export async function send_tx_options(options:Options):Promise{ const message = txSkeleton.get("signingEntries").get(0)?.message; const Sig = hd.key.signRecoverable(message!, options.privKey); const tx = helpers.sealTransaction(txSkeleton, [Sig]); - const hash = await defaultRpc.send_transaction(tx, "passthrough"); + const hash = await defaultRpc.sendTransaction(tx, "passthrough"); console.log("The transaction hash is", hash); return hash; @@ -311,82 +440,65 @@ export async function send_tx_options(options:Options):Promise{ export async function send_tx(options: Options): Promise { let txSkeleton = helpers.TransactionSkeleton({}); - const fromScript = helpers.parseAddress(options.from, { config: AGGRON4 }); - // const toScript = helpers.parseAddress(options.to, { config: AGGRON4 }); + const fromScript = helpers.parseAddress(options.from, {config: AGGRON4}); - // additional 0.001 ckb for tx fee - // the tx fee could calculated by tx size - // this is just a simple example let neededCapacity = BI.from(0) for (let i = 0; i < options.outputCells.length; i++) { - neededCapacity = neededCapacity.add(options.outputCells[i].cell_output.capacity) + neededCapacity = neededCapacity.add(options.outputCells[i].cellOutput.capacity) } let collectedSum = BI.from(0); const collected: Cell[] = []; - const collector = indexer.collector({ lock: fromScript, type: "empty" }); + const collector = defaultIndexer.collector({lock: fromScript, type: "empty"}); for await (const cell of collector.collect()) { - collectedSum = collectedSum.add(cell.cell_output.capacity); + collectedSum = collectedSum.add(cell.cellOutput.capacity); collected.push(cell); if (collectedSum >= neededCapacity) break; } - console.log('total cell balance: ',collectedSum.toString()) + console.log('total cell balance: ', collectedSum.toString()) if (collectedSum < neededCapacity) { throw new Error("Not enough CKB"); } - if(collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).gt(BI.from('0'))) { + if (collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).gt(BI.from('0'))) { const changeOutput: Cell = { - cell_output: { + cellOutput: { capacity: collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).toHexString(), lock: fromScript, }, data: "0x", }; console.log('gen out put extra value ') - txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(changeOutput)); + if (collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).gt(6100000000)) { + //expected occupied capacity (0x16b969d00) + txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(changeOutput)); + } else { + options.outputCells[0].cellOutput.capacity = collectedSum.sub(neededCapacity).sub(FeeRate.NORMAL).add(options.outputCells[0].cellOutput.capacity).toHexString() + } } + + let SECP256K1_BLAKE160_HASH = (await defaultRpc.getBlockByNumber("0x0")).transactions[1].hash + txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(...options.outputCells)); txSkeleton = txSkeleton.update("inputs", (inputs) => inputs.push(...collected)); txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => cellDeps.push(...[ { - out_point: { - tx_hash: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, - index: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.INDEX, - }, - dep_type: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, - } - ,{ - out_point: { - tx_hash: AGGRON4.SCRIPTS.SUDT.TX_HASH, - index: AGGRON4.SCRIPTS.SUDT.INDEX, - }, - dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, - }, - // { - // out_point: { - // tx_hash: ANY_ONE_CAN_PAY_TYPE_ID.TX_HASH, - // index: ANY_ONE_CAN_PAY_TYPE_ID.INDEX, - // }, - // dep_type: AGGRON4.SCRIPTS.SUDT.DEP_TYPE, - // } - // { - // out_point: { - // tx_hash: ANY_ONE_CAN_PAY.TX_HASH, - // index: ANY_ONE_CAN_PAY.INDEX, - // }, - // dep_type: "code", - // } + outPoint: { + txHash: SECP256K1_BLAKE160_HASH, + index: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.INDEX, + }, + depType: AGGRON4.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, + } ]) ); - txSkeleton = txSkeleton.update("cellDeps",(cellDeps)=> cellDeps.push(...options.deps)); + txSkeleton = txSkeleton.update("cellDeps", (cellDeps) => cellDeps.push(...options.deps)); const firstIndex = txSkeleton .get("inputs") .findIndex((input) => - new ScriptValue(input.cell_output.lock, { validate: false }).equals( - new ScriptValue(fromScript, { validate: false }) + new ScriptValue(input.cellOutput.lock, {validate: false}).equals( + new ScriptValue(fromScript, {validate: false}) ) ); if (firstIndex !== -1) { @@ -400,23 +512,23 @@ export async function send_tx(options: Options): Promise { "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }; if (witness !== "0x") { - const witnessArgs = new core.WitnessArgs(new toolkit.Reader(witness)); - const lock = witnessArgs.getLock(); - if (lock.hasValue() && new toolkit.Reader(lock.value().raw()).serializeJson() !== newWitnessArgs.lock) { + const witnessArgs = blockchain.WitnessArgs.unpack(bytes.bytify(witness)) + const lock = witnessArgs.lock; + if (!!lock && lock !== newWitnessArgs.lock) { throw new Error("Lock field in first witness is set aside for signature!"); } - const inputType = witnessArgs.getInputType(); - if (inputType.hasValue()) { - newWitnessArgs.input_type = new toolkit.Reader(inputType.value().raw()).serializeJson(); + const inputType = witnessArgs.inputType; + if (!!inputType) { + newWitnessArgs.inputType = inputType; } - const outputType = witnessArgs.getOutputType(); - if (outputType.hasValue()) { - newWitnessArgs.output_type = new toolkit.Reader(outputType.value().raw()).serializeJson(); + const outputType = witnessArgs.outputType; + if (!!outputType) { + newWitnessArgs.outputType = outputType; } } - witness = new toolkit.Reader( - core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs)) - ).serializeJson(); + witness = bytes.hexify(blockchain.WitnessArgs.pack(newWitnessArgs)) + + txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(firstIndex, witness)); } @@ -426,15 +538,16 @@ export async function send_tx(options: Options): Promise { const Sig = hd.key.signRecoverable(message!, options.privKey); const tx = helpers.sealTransaction(txSkeleton, [Sig]); - if( options.lightMode == true){ - if(options.lightNotInstallCellMode == null||options.lightNotInstallCellMode == false){ + if (options.lightMode == true) { + if (options.lightNotInstallCellMode == null || options.lightNotInstallCellMode == false) { await installTxCells(tx) } - const hash = await sendTransaction(tx,CKB_LIGHT_RPC_URL) + console.log("send tx:", tx) + const hash = await sendTransaction(tx, CKB_LIGHT_RPC_URL) console.log("The CKB_LIGHT_RPC_URL transaction hash is", hash); return hash; } - const hash = await defaultRpc.send_transaction(tx, "passthrough"); + const hash = await defaultRpc.sendTransaction(tx, "passthrough"); console.log("The transaction hash is", hash); return hash; @@ -442,15 +555,16 @@ export async function send_tx(options: Options): Promise { } -async function installTxCells(tx:Transaction) { +async function installTxCells(tx: Transaction) { + // install dep hash - for (let i = 0; i < tx.cell_deps.length; i++) { - let dep = tx.cell_deps[i].out_point - await fetchTransactionUntilFetched(dep.tx_hash,CKB_LIGHT_RPC_URL,100) + for (let i = 0; i < tx.cellDeps.length; i++) { + let dep = tx.cellDeps[i].outPoint + await fetchTransactionUntilFetched(dep.txHash, CKB_LIGHT_RPC_URL, 100) } // install cell - let scrips:ScriptMsg[] = [] + let scrips: LightClientScript[] = [] let minBlockNum = BI.from("0xffffffffffff") let heightBlockNum = BI.from("0x0") @@ -459,43 +573,43 @@ async function installTxCells(tx:Transaction) { // get input scripts scrips.push({ - script:await getScriptByOutPut(tx.inputs[i].previous_output), - script_type:"lock", - block_number:"0x1" + script: await getScriptByOutPut(tx.inputs[i].previousOutput), + scriptType: "lock", + blockNumber: "0x1" }) // get min block num - let blockNum = await getBlockNumByTxHash(tx.inputs[i].previous_output.tx_hash) - if(minBlockNum.gt(blockNum)){ + let blockNum = await getBlockNumByTxHash(tx.inputs[i].previousOutput.txHash) + if (minBlockNum.gt(blockNum)) { minBlockNum = blockNum } // get height block num - if(heightBlockNum.lt(blockNum)){ + if (heightBlockNum.lt(blockNum)) { heightBlockNum = blockNum } } - for (let i = 0; i { - let cell = await rpcCLient.get_live_cell(previous_output,false) - console.log('cell:',cell) - if (cell.status == 'unknown'){ +async function getScriptByOutPut(previous_output: OutPoint): Promise