diff --git a/cicd/test_script/steps/add-new-peer-org.sh b/cicd/test_script/steps/add-new-peer-org.sh index 1bd573c8..9c749a41 100644 --- a/cicd/test_script/steps/add-new-peer-org.sh +++ b/cicd/test_script/steps/add-new-peer-org.sh @@ -68,7 +68,8 @@ sleep 5 # [orgnew] deploy chaincode export_env 'peer' ${PEER_ORG_NAME_ORGNEW} ${PEER_ORG_DOMAIN_ORGNEW} 'peer0' -bdk chaincode deploy -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I -a --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} +bdk chaincode install -l ${CHAINCODE_LABEL} +bdk chaincode approve -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I # discover # [orgnew] install only export_env 'peer' ${PEER_ORG_NAME_ORGNEW} ${PEER_ORG_DOMAIN_ORGNEW} 'peer1' diff --git a/cicd/test_script/steps/chaincode.sh b/cicd/test_script/steps/chaincode.sh index 27d6c03c..edc23147 100644 --- a/cicd/test_script/steps/chaincode.sh +++ b/cicd/test_script/steps/chaincode.sh @@ -3,23 +3,25 @@ bdk chaincode package -n fabcar -v 1 -p ./chaincode/fabcar/go # deploy export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer0' -bdk chaincode deploy -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I -a --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} +bdk chaincode install -l ${CHAINCODE_LABEL} +bdk chaincode approve -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} # without discover export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer1' bdk chaincode install -l ${CHAINCODE_LABEL} export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer2' bdk chaincode install -l ${CHAINCODE_LABEL} export_env 'peer' ${PEER_ORG_NAME_ORG1} ${PEER_ORG_DOMAIN_ORG1} 'peer0' -bdk chaincode deploy -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I -a --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} +bdk chaincode install -l ${CHAINCODE_LABEL} +bdk chaincode approve -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I # discover export_env 'peer' ${PEER_ORG_NAME_ORG2} ${PEER_ORG_DOMAIN_ORG2} 'peer0' bdk chaincode install -l ${CHAINCODE_LABEL} -bdk chaincode approve -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} -bdk chaincode commit -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I --orderer ${ORDERER_ORG_URL_ORG0_ORDERER0} --peer-addresses ${PEER_ORG_URL_ORG0_PEER0} --peer-addresses ${PEER_ORG_URL_ORG1_PEER0} --peer-addresses ${PEER_ORG_URL_ORG2_PEER0} +bdk chaincode approve -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I # discover +bdk chaincode commit -C ${CHANNEL_NAME} -l ${CHAINCODE_LABEL} -I # discover # invoke init export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer0' -bdk chaincode invoke -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -I -f InitLedger --orderer ${ORDERER_ORG_URL_ORG1_ORDERER0} --peer-addresses ${PEER_ORG_URL_ORG0_PEER0} --peer-addresses ${PEER_ORG_URL_ORG1_PEER0} --peer-addresses ${PEER_ORG_URL_ORG2_PEER0} +bdk chaincode invoke -C ${CHANNEL_NAME} -n ${CHAINCODE_NAME} -I -f InitLedger # discover # [Org0] invoke & query export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer0' diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 879f0d1a..bce10297 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -97,47 +97,30 @@ Description: Enroll certificates. Description: 代表環境變數中 BDK_ORG_NAME 的 Peer org 同意 Chaincode -| Options | Type | Description | Required | Default | -| --------------------- | :-----: | -------------------------------------- | :------: | ------- | -| --help | boolean | Show help | | -| --version | | boolean Show version number | | -| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | -| -C, --channel-id | string | 選擇欲同意 Chaincode 在的 Channel 名稱 | | -| -l, --chaincode-label | string | Chaincode package 的標籤名稱 | | -| -I, --init-required | boolean | Chaincode 是否需要初始化 | | -| --orderer | string | 選擇 Orderer 同意 Chaincode | | +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ----------------------------------------------------- | :------: | ------- | +| --help | boolean | Show help | | | +| --version | | boolean Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | +| -C, --channel-id | string | 選擇欲同意 Chaincode 在的 Channel 名稱 | V | | +| -l, --chaincode-label | string | Chaincode package 的標籤名稱 | V | | +| -I, --init-required | boolean | Chaincode 是否需要初始化 | | | +| --orderer | string | 選擇 Orderer 同意 Chaincode (若未輸入則使用discover) | | | ### `bdk chaincode commit` Description: 代表環境變數中 BDK_ORG_NAME 的 Peer org 發布 Chaincode -| Options | Type | Description | Required | Default | -| --------------------- | :-----: | -------------------------------------- | :------: | ------- | -| --help | boolean | Show help | | -| --version | boolean | Show version number | | -| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | -| -C, --channel-id | string | 選擇欲發布 Chaincode 在的 Channel 名稱 | | -| -l, --chaincode-label | string | Chaincode package 的標籤名稱 | | -| -I, --init-required | boolean | Chaincode 是否需要初始化 | | -| --orderer | string | 選擇 Orderer 同意 Chaincode | | -| --peer-addresses | array | 需要簽名的 Peer address | | - -### `bdk chaincode deploy` - -Description: 部署 / 更新 Chaincode - -| Options | Type | Description | Required | Default | -| --------------------- | :-----: | -------------------------------------- | :------: | ------- | -| --help | boolean | Show help | | -| --version | boolean | Show version number | | -| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | -| -C, --channel-id | string | 選擇欲部署 Chaincode 在的 Channel 名稱 | | -| -l, --chaincode-label | string | Chaincode package 的標籤名稱 | | -| -a, --approve-only | boolean | 是否只需要做到同意的步驟 | | -| -c, --commit-only | boolean | 是否只需要做到部署的步驟 | | -| -I, --init-required | boolean | Chaincode 是否需要初始化 | | -| --orderer | string | 選擇 Orderer 部署 Chaincode | | -| --peer-addresses | array | 需要簽名的 Peer address | | +| Options | Type | Description | Required | Default | +| --------------------- | :-----: | ---------------------------------------------------- | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | +| -C, --channel-id | string | 選擇欲發布 Chaincode 在的 Channel 名稱 | | | +| -l, --chaincode-label | string | Chaincode package 的標籤名稱 | | | +| -I, --init-required | boolean | Chaincode 是否需要初始化 | | | +| --orderer | string | 選擇 Orderer 同意 Chaincode (若未輸入則使用discover) | | | +| --peer-addresses | array | 需要簽名的 Peer address (若未輸入則使用discover) | | | ### `bdk chaincode install` @@ -154,18 +137,18 @@ Description: 安裝 Chaincode Description: 執行 Chaincode function -| Options | Type | Description | Required | Default | -| ------------------------ | :-----: | -------------------------------------- | :------: | ------- | -| --help | boolean | Show help | | -| --version | boolean | Show version number | | -| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | -| -C, --channel-id | string | 選擇欲執行 Chaincode 在的 Channel 名稱 | | -| -n, --chaincode-name | string | 欲執行 Chaincode 的名稱 | | -| -I, --is-init | boolean | 是否要初始化 Chaincode | | false | -| -f, --chaincode-function | string | 執行 Chaincode 的 function | | -| -a, --args | array | 執行 Chaincode 需要的參數 | | -| --orderer | string | 選擇 Orderer 執行 Chaincode | | -| --peer-addresses | array | 需要簽名的 Peer address | | +| Options | Type | Description | Required | Default | +| ------------------------ | :-----: | ---------------------------------------------------- | :------: | ------- | +| --help | boolean | Show help | | | +| --version | boolean | Show version number | | | +| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | | | +| -C, --channel-id | string | 選擇欲執行 Chaincode 在的 Channel 名稱 | | | +| -n, --chaincode-name | string | 欲執行 Chaincode 的名稱 | | | +| -I, --is-init | boolean | 是否要初始化 Chaincode | | false | +| -f, --chaincode-function | string | 執行 Chaincode 的 function | | | +| -a, --args | array | 執行 Chaincode 需要的參數 | | | +| --orderer | string | 選擇 Orderer 執行 Chaincode (若未輸入則使用discover) | | | +| --peer-addresses | array | 需要簽名的 Peer address (若未輸入則使用discover) | | | ### `bdk chaincode package` diff --git a/docs/EXAMPLE-EN.md b/docs/EXAMPLE-EN.md index b9704287..c5b03e9f 100644 --- a/docs/EXAMPLE-EN.md +++ b/docs/EXAMPLE-EN.md @@ -213,14 +213,16 @@ bdk chaincode package -n fabcar -v 1 -p ./chaincode/fabcar/go # export BDK_HOSTNAME='peer0' # Install and approve chaincode on peer0 in Org1 -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 # export BDK_ORG_NAME='Org2' # export BDK_ORG_DOMAIN='org2.example.com' # export BDK_HOSTNAME='peer0' # Install and approve chaincode on peer0 in Org2 -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 ``` ### Step 3: Install chaincode on peer1 in Org1 @@ -245,7 +247,7 @@ Deploys the chaincode labelled *fabcar_1*. Parameter `-c` is passed to restrict # export BDK_ORG_DOMAIN='org1.example.com' # export BDK_HOSTNAME='peer0' -bdk chaincode deploy -C test -l fabcar_1 -I -c --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:7151 +bdk chaincode commit -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:7151 ``` ### Step 5: Initial chaincode from Org1 @@ -513,7 +515,8 @@ Install and approve the chaincode named fabcar_1. Since we are using the blockch # export BDK_HOSTNAME='peer0' # Install and approve chaincode on peer0 of Org3 -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 # export BDK_ORG_NAME='Org3' # export BDK_ORG_DOMAIN='org3.example.com' diff --git a/docs/EXAMPLE.md b/docs/EXAMPLE.md index 13c43054..5f2197cb 100644 --- a/docs/EXAMPLE.md +++ b/docs/EXAMPLE.md @@ -213,14 +213,16 @@ bdk chaincode package -n fabcar -v 1 -p ./chaincode/fabcar/go # export BDK_HOSTNAME='peer0' # Org1 的 peer0 安裝、同意 Chaincode -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 # export BDK_ORG_NAME='Org2' # export BDK_ORG_DOMAIN='org2.example.com' # export BDK_HOSTNAME='peer0' # Org2 的 peer0 安裝、同意 Chaincode -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 ``` ### Step 3:Org1 的 peer1 安裝 Chaincode @@ -245,7 +247,7 @@ bdk chaincode install -l fabcar_1 # export BDK_ORG_DOMAIN='org1.example.com' # export BDK_HOSTNAME='peer0' -bdk chaincode deploy -C test -l fabcar_1 -I -c --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:7151 +bdk chaincode commit -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:7151 ``` ### Step 5:Org1 初始化 Chaincode @@ -513,7 +515,8 @@ bdk channel join -n test --orderer orderer0.example.com:7050 # export BDK_HOSTNAME='peer0' # Org3 的 peer0 安裝、同意 Chaincode -bdk chaincode deploy -C test -l fabcar_1 -I -a --orderer orderer0.example.com:7050 +bdk chaincode install -l fabcar_1 +bdk chaincode approve -C test -l fabcar_1 -I --orderer orderer0.example.com:7050 # export BDK_ORG_NAME='Org3' # export BDK_ORG_DOMAIN='org3.example.com' diff --git a/package-lock.json b/package-lock.json index 9f62d4f1..754c2bcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "envfile": "^6.14.0", "fs-extra": "^9.1.0", "js-yaml": "^4.1.0", - "memory-streams": "^0.1.3", "prompts": "^2.4.0", "string-format": "^2.0.0", "winston": "^3.3.3", @@ -3751,7 +3750,8 @@ "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", @@ -4123,30 +4123,6 @@ "node": ">= 12" } }, - "node_modules/memory-streams": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/memory-streams/-/memory-streams-0.1.3.tgz", - "integrity": "sha512-qVQ/CjkMyMInPaaRMrwWNDvf6boRZXaT/DbQeMYcCWuXPEBf1v8qChOc9OlEVQp2uOvRXa1Qu30fLmKhY6NipA==", - "dependencies": { - "readable-stream": "~1.0.2" - } - }, - "node_modules/memory-streams/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/memory-streams/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9136,7 +9112,8 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -9432,32 +9409,6 @@ "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true }, - "memory-streams": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/memory-streams/-/memory-streams-0.1.3.tgz", - "integrity": "sha512-qVQ/CjkMyMInPaaRMrwWNDvf6boRZXaT/DbQeMYcCWuXPEBf1v8qChOc9OlEVQp2uOvRXa1Qu30fLmKhY6NipA==", - "requires": { - "readable-stream": "~1.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index 83f100a0..886c31aa 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "envfile": "^6.14.0", "fs-extra": "^9.1.0", "js-yaml": "^4.1.0", - "memory-streams": "^0.1.3", "prompts": "^2.4.0", "string-format": "^2.0.0", "winston": "^3.3.3", diff --git a/src/command/chaincode/approve.ts b/src/command/chaincode/approve.ts index 5551c523..5013f0c4 100644 --- a/src/command/chaincode/approve.ts +++ b/src/command/chaincode/approve.ts @@ -1,7 +1,7 @@ import { Arguments, Argv } from 'yargs' import prompts from 'prompts' import { logger, onCancel } from '../../util' -import { ChaincodeApproveType } from '../../model/type/chaincode.type' +import { ChaincodeApproveType, ChaincodeApproveWithoutDiscoverType } from '../../model/type/chaincode.type' import Chaincode from '../../service/chaincode' import Channel from '../../service/channel' import config from '../../config' @@ -25,12 +25,13 @@ const ordererList = getOrdererList(config) export const builder = (yargs: Argv) => { return yargs .example('bdk chaincode approve --interactive', 'Cathay BDK 互動式問答') - .example('bdk chaincode approve --channel-id fabcar --chaincode-label test_1 --init-required --orderer orderer0.example.com:7050', '使用 orderer0.example.com:7050 同意名稱為 test 中標籤為 test_1 的 Chaincode,此 Chaincode 需要初始化') + .example('bdk chaincode approve --channel-id test --chaincode-label fabcar_1 --init-required', '使用 discover 自動選擇 orderer 同意名稱為 test 的 channel 中標籤為 fabcar_1 的 Chaincode,此 Chaincode 需要初始化') + .example('bdk chaincode approve --channel-id test --chaincode-label fabcar_1 --init-required --orderer orderer0.example.com:7050', '使用 orderer0.example.com:7050 同意名稱為 test 的 channel 中標籤為 fabcar_1 的 Chaincode,此 Chaincode 需要初始化') .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) .option('channel-id', { type: 'string', description: '選擇欲同意 Chaincode 在的 Channel 名稱', alias: 'C' }) .option('chaincode-label', { type: 'string', description: 'Chaincode package 的標籤名稱', alias: 'l', choices: chaincodeList.map(x => `${x.name}_${x.version}`) }) .option('init-required', { type: 'boolean', description: 'Chaincode 是否需要初始化', alias: 'I' }) - .option('orderer', { type: 'string', choices: ordererList, description: '選擇 Orderer 同意 Chaincode' }) + .option('orderer', { type: 'string', choices: ordererList, description: '選擇 Orderer 同意 Chaincode (若未輸入則使用discover)' }) } export const handler = async (argv: Arguments) => { @@ -39,7 +40,7 @@ export const handler = async (argv: Arguments) => { const chaincode = new Chaincode(config) const channel = new Channel(config) - let approveChannelInput: ChaincodeApproveType + let approveChannelInput: ChaincodeApproveType | ChaincodeApproveWithoutDiscoverType const chaincodeVersionMap: Map = new Map() chaincodeList.forEach(chaincode => { chaincodeVersionMap.set(chaincode.name, [...(chaincodeVersionMap.get(chaincode.name) || []), chaincode.version]) @@ -89,26 +90,45 @@ export const handler = async (argv: Arguments) => { }, ], { onCancel }) - const channelGroup = await channel.getChannelGroup(channelId) - - const { orderer } = await prompts([ - { - type: 'select', - name: 'orderer', - message: 'Ordering service endpoint', - choices: channelGroup.orderer.map(x => ({ - title: x, - value: x, - })), - }, - ], { onCancel }) approveChannelInput = { channelId, chaincodeName, chaincodeVersion, initRequired, - orderer, + } + + const discoverOrderer = await prompts([ + { + type: 'select', + name: 'discoverOrderer', + message: 'Set orderer with discover?', + choices: [ + { + title: 'Yes', + value: true, + }, + { + title: 'No', + value: false, + }, + ], + }, + ], { onCancel }) + if (discoverOrderer) { + const channelGroup = await channel.getChannelGroup(channelId) + const { orderer } = await prompts([ + { + type: 'select', + name: 'orderer', + message: 'Ordering service endpoint', + choices: channelGroup.orderer.map(x => ({ + title: x, + value: x, + })), + }, + ], { onCancel }) + approveChannelInput = { ...approveChannelInput, orderer } } } else { approveChannelInput = { @@ -116,7 +136,9 @@ export const handler = async (argv: Arguments) => { chaincodeName: argv.chaincodeLabel.split('_')[0], chaincodeVersion: parseInt(argv.chaincodeLabel.split('_')[1]), initRequired: argv.initRequired, - orderer: argv.orderer, + } + if (argv.orderer) { + approveChannelInput = { ...approveChannelInput, orderer: argv.orderer } } } await chaincode.approve(approveChannelInput) diff --git a/src/command/chaincode/commit.ts b/src/command/chaincode/commit.ts index eae3b4f9..d6e1735c 100644 --- a/src/command/chaincode/commit.ts +++ b/src/command/chaincode/commit.ts @@ -1,7 +1,7 @@ import { Arguments, Argv } from 'yargs' import prompts from 'prompts' import { logger, onCancel } from '../../util' -import { ChaincodeCommitType } from '../../model/type/chaincode.type' +import { ChaincodeCommitType, ChaincodeCommitWithoutDiscoverType } from '../../model/type/chaincode.type' import Chaincode from '../../service/chaincode' import Channel from '../../service/channel' import config from '../../config' @@ -24,13 +24,14 @@ const chaincodeList = getChaincodeList(config) export const builder = (yargs: Argv) => { return yargs .example('bdk chaincode commit --interactive', 'Cathay BDK 互動式問答') - .example('bdk chaincode commit --channel-id fabcar --chaincode-label test_1 --init-required --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com --peer-addresses peer0.org2.example.com', '使用 orderer0.example.com:7050 同意名稱為 test 中標籤為 test_1 的 Chaincode,此 Chaincode 需要初始化並且 Org1 和 Org2 的 Peer org 簽名') + .example('bdk chaincode commit --channel-id test --chaincode-label fabcar_1 --init-required', '使用 discover 自動選擇 peer 與 orderer 同意名稱為 test 的 channel 中標籤為 fabcar_1 的 Chaincode,此 Chaincode 需要初始化並且 Org1 和 Org2 的 Peer org 簽名') + .example('bdk chaincode commit --channel-id test --chaincode-label fabcar_1 --init-required --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com --peer-addresses peer0.org2.example.com', '使用 orderer0.example.com:7050 同意名稱為 test 的 channel 中標籤為 fabcar_1 的 Chaincode,此 Chaincode 需要初始化並且 Org1 和 Org2 的 Peer org 簽名') .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) .option('channel-id', { type: 'string', description: '選擇欲發布 Chaincode 在的 Channel 名稱', alias: 'C' }) .option('chaincode-label', { type: 'string', description: 'Chaincode package 的標籤名稱', alias: 'l', choices: chaincodeList.map(x => `${x.name}_${x.version}`) }) .option('init-required', { type: 'boolean', description: 'Chaincode 是否需要初始化', alias: 'I' }) - .option('orderer', { type: 'string', description: '選擇 Orderer 同意 Chaincode' }) - .option('peer-addresses', { type: 'array', description: '需要簽名的 Peer address' }) + .option('orderer', { type: 'string', description: '選擇 Orderer 同意 Chaincode (若未輸入則使用discover)' }) + .option('peer-addresses', { type: 'array', description: '需要簽名的 Peer address (若未輸入則使用discover)' }) } export const handler = async (argv: Arguments) => { @@ -39,7 +40,7 @@ export const handler = async (argv: Arguments) => { const chaincode = new Chaincode(config) const channel = new Channel(config) - let commitChannelInput: ChaincodeCommitType + let commitChannelInput: ChaincodeCommitType | ChaincodeCommitWithoutDiscoverType const chaincodeVersionMap: Map = new Map() chaincodeList.forEach(chaincode => { chaincodeVersionMap.set(chaincode.name, [...(chaincodeVersionMap.get(chaincode.name) || []), chaincode.version]) @@ -89,36 +90,78 @@ export const handler = async (argv: Arguments) => { }, ], { onCancel }) - const channelGroup = await channel.getChannelGroup(channelId) - const { orderer, peerAddresses } = await prompts([ + commitChannelInput = { + channelId, + chaincodeName, + chaincodeVersion, + initRequired, + } + + const discoverOrderer = await prompts([ { type: 'select', - name: 'orderer', - message: 'Ordering service endpoint', - choices: channelGroup.orderer.map(x => ({ - title: x, - value: x, - })), + name: 'discoverOrderer', + message: 'Set orderer with discover?', + choices: [ + { + title: 'Yes', + value: true, + }, + { + title: 'No', + value: false, + }, + ], }, + ], { onCancel }) + if (discoverOrderer) { + const channelGroup = await channel.getChannelGroup(channelId) + const { orderer } = await prompts([ + { + type: 'select', + name: 'orderer', + message: 'Ordering service endpoint', + choices: channelGroup.orderer.map(x => ({ + title: x, + value: x, + })), + }, + ], { onCancel }) + commitChannelInput = { ...commitChannelInput, orderer } + } + + const discoverPeer = await prompts([ { - type: 'multiselect', - name: 'peerAddresses', - message: 'The addresses of the peers to connect to', - choices: channelGroup.anchorPeer.map(x => ({ - title: x, - value: x, - })), + type: 'select', + name: 'discoverPeer', + message: 'Set peers with discover?', + choices: [ + { + title: 'Yes', + value: true, + }, + { + title: 'No', + value: false, + }, + ], }, ], { onCancel }) - - commitChannelInput = { - channelId, - chaincodeName, - chaincodeVersion, - initRequired, - orderer, - peerAddresses, + if (discoverPeer) { + const channelGroup = await channel.getChannelGroup(channelId) + const { peerAddresses } = await prompts([ + { + type: 'multiselect', + name: 'peerAddresses', + message: 'The addresses of the peers to connect to', + choices: channelGroup.anchorPeer.map(x => ({ + title: x, + value: x, + })), + }, + ], { onCancel }) + commitChannelInput = { ...commitChannelInput, peerAddresses } } } else { commitChannelInput = { @@ -126,8 +169,12 @@ export const handler = async (argv: Arguments) => { chaincodeName: argv.chaincodeLabel.split('_')[0], chaincodeVersion: parseInt(argv.chaincodeLabel.split('_')[1]), initRequired: argv.initRequired, - orderer: argv.orderer, - peerAddresses: argv.peerAddresses, + } + if (argv.orderer) { + commitChannelInput = { ...commitChannelInput, orderer: argv.orderer } + } + if (argv.peerAddresses) { + commitChannelInput = { ...commitChannelInput, peerAddresses: argv.peerAddresses } } } diff --git a/src/command/chaincode/deploy.ts b/src/command/chaincode/deploy.ts deleted file mode 100644 index ac0fed3f..00000000 --- a/src/command/chaincode/deploy.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { Arguments, Argv } from 'yargs' -import prompts from 'prompts' -import { logger, onCancel } from '../../util' -import { ChaincodeDeployType } from '../../model/type/chaincode.type' -import Chaincode from '../../service/chaincode' -import Channel from '../../service/channel' -import config from '../../config' -import { getChaincodeList, joinedChannelChoice } from '../../model/prompts/util' - -export const command = 'deploy' - -export const desc = '部署 / 更新 Chaincode' - -interface OptType { - interactive: boolean - channelId: string - chaincodeLabel: string - approveOnly: boolean - commitOnly: boolean - initRequired: boolean - orderer: string - peerAddresses: string[] -} - -const chaincodeList = getChaincodeList(config) -export const builder = (yargs: Argv) => { - return yargs - .example('bdk chaincode deploy --interactive', 'Cathay BDK 互動式問答') - .example('bdk chaincode deploy --channel-id fabcar --chaincode-label test_1 --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:8051', '使用 orderer0.example.com:7050 部署名稱為 test 中標籤為 test_1 的 Chaincode,此 Chaincode 需要初始化並且 Org1 和 Org2 的 Peer org 簽名(包含安裝、同意、部署、初始 Chaincode 步驟)') - .example('bdk chaincode deploy --channel-id fabcar --chaincode-label test_1 --init-required --approve-only --orderer orderer0.example.com:7050', '使用 orderer0.example.com:7050 同意名稱為 test 中標籤為 test_1 的 Chaincode,此 Chaincode 需要初始化(包含安裝、同意 Chaincode 步驟)') - .example('bdk chaincode deploy --channel-id fabcar --chaincode-label test_1 --init-required --commit-only --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:8051', '使用 orderer0.example.com:7050 部署名稱為 test 中標籤為 test_1 的 Chaincode,此 Chaincode 需要初始化並且 Org1 和 Org2 的 Peer org 簽名(包含安裝、同意、部署 Chaincode 步驟)') - .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) - .option('channel-id', { type: 'string', description: '選擇欲部署 Chaincode 在的 Channel 名稱', alias: 'C' }) - .option('chaincode-label', { type: 'string', description: 'Chaincode package 的標籤名稱', alias: 'l', choices: chaincodeList.map(x => `${x.name}_${x.version}`) }) - .option('approve-only', { type: 'boolean', description: '是否只需要做到同意的步驟', alias: 'a' }) - .option('commit-only', { type: 'boolean', description: '是否只需要做到部署的步驟', alias: 'c' }) - .option('init-required', { type: 'boolean', description: 'Chaincode 是否需要初始化', alias: 'I' }) - .option('orderer', { type: 'string', description: '選擇 Orderer 部署 Chaincode' }) - .option('peer-addresses', { type: 'array', description: '需要簽名的 Peer address' }) -} - -export const handler = async (argv: Arguments) => { - logger.debug('exec chaincode deploy') - - const chaincode = new Chaincode(config) - const channel = new Channel(config) - - let deployChannelInput: ChaincodeDeployType - const chaincodeVersionMap: Map = new Map() - chaincodeList.forEach(chaincode => { - chaincodeVersionMap.set(chaincode.name, [...(chaincodeVersionMap.get(chaincode.name) || []), chaincode.version]) - }) - if (argv.interactive) { - const { channelId, chaincodeName } = await prompts([ - { - type: 'select', - name: 'channelId', - message: 'Which channel do you want deploy chaincode', - choices: await joinedChannelChoice(channel), - }, - { - type: 'select', - name: 'chaincodeName', - message: 'What is your chaincode name?', - choices: Array.from(chaincodeVersionMap.keys()).map(x => ({ - title: x, - value: x, - })), - }, - ], { onCancel }) - const { chaincodeVersion, initRequired, approveOrCommit } = await prompts([ - { - type: 'select', - name: 'chaincodeVersion', - message: 'What is your chaincode version?', - choices: (chaincodeVersionMap.get(chaincodeName) || []).sort((a, b) => (a - b)).map(x => ({ - title: x.toString(), - value: x, - })), - }, - { - type: 'select', - name: 'initRequired', - message: 'Whether the chaincode requires invoking \'init\'', - choices: [ - { - title: 'true', - value: true, - }, - { - title: 'false', - value: false, - }, - ], - - }, - { - type: 'multiselect', - name: 'approveOrCommit', - message: 'Do approve or do commit?', - choices: [ - { - title: 'Do approve', - value: 'approve', - }, - { - title: 'Do commit', - value: 'commit', - }, - ], - }, - ], { onCancel }) - - const channelGroup = await channel.getChannelGroup(channelId) - - const { orderer } = await prompts([ - { - type: 'select', - name: 'orderer', - message: 'Ordering service endpoint', - choices: channelGroup.orderer.map(x => ({ - title: x, - value: x, - })), - }, - ], { onCancel }) - const peerAddresses = approveOrCommit.includes('commit') - ? (await prompts([{ - type: 'multiselect', - name: 'peerAddresses', - message: 'The addresses of the peers to connect to', - choices: channelGroup.anchorPeer.map(x => ({ - title: x, - value: x, - })), - }], { onCancel })).peerAddresses - : [] - - deployChannelInput = { - channelId, - chaincodeName, - chaincodeVersion, - approve: approveOrCommit.includes('approve'), - commit: approveOrCommit.includes('commit'), - initRequired, - orderer, - peerAddresses, - } - } else { - deployChannelInput = { - channelId: argv.channelId, - chaincodeName: argv.chaincodeLabel.split('_')[0], - chaincodeVersion: parseInt(argv.chaincodeLabel.split('_')[1].split('.')[0], 10), - approve: argv.approveOnly || (!argv.commitOnly), - commit: argv.commitOnly || (!argv.approveOnly), - initRequired: argv.initRequired, - orderer: argv.orderer, - peerAddresses: argv.peerAddresses, - } - } - - if (deployChannelInput.approve) { - const chaincodeLabel = `${deployChannelInput.chaincodeName}_${deployChannelInput.chaincodeVersion}` - await chaincode.install({ chaincodeLabel }) - await chaincode.approve({ - channelId: deployChannelInput.channelId, - chaincodeName: deployChannelInput.chaincodeName, - chaincodeVersion: deployChannelInput.chaincodeVersion, - initRequired: deployChannelInput.initRequired, - orderer: deployChannelInput.orderer, - }) - } - if (deployChannelInput.commit) { - await chaincode.commit({ - channelId: deployChannelInput.channelId, - chaincodeName: deployChannelInput.chaincodeName, - chaincodeVersion: deployChannelInput.chaincodeVersion, - initRequired: deployChannelInput.initRequired, - orderer: deployChannelInput.orderer, - peerAddresses: deployChannelInput.peerAddresses || [], - }) - } -} diff --git a/src/command/chaincode/invoke.ts b/src/command/chaincode/invoke.ts index bf598629..9fdc3b3a 100644 --- a/src/command/chaincode/invoke.ts +++ b/src/command/chaincode/invoke.ts @@ -1,7 +1,7 @@ import { Argv, Arguments } from 'yargs' import prompts from 'prompts' import { logger, onCancel } from '../../util' -import { ChaincodeInvokeType } from '../../model/type/chaincode.type' +import { ChaincodeInvokeType, ChaincodeInvokeWithoutDiscoverType } from '../../model/type/chaincode.type' import Channel from '../../service/channel' import Chaincode from '../../service/chaincode' import { committedChaincodeChoice, joinedChannelChoice } from '../../model/prompts/util' @@ -25,6 +25,7 @@ interface OptType { export const builder = (yargs: Argv) => { return yargs .example('bdk chaincode invoke --interactive', 'Cathay BDK 互動式問答') + .example('bdk chaincode invoke --channel-id fabcar --chaincode-name fabcar --is-init --chaincode-function InitLedger', '使用名稱為 InitLedger 的 function 初始化名稱為 fabcar 的 Chaincode,使用 discover 決定 orderer 與 peer') .example('bdk chaincode invoke --channel-id fabcar --chaincode-name fabcar --is-init --chaincode-function InitLedger --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:8051', '使用 orderer0.example.com:7050 和名稱為 InitLedger 的 function 初始化名稱為 fabcar 的 Chaincode,需要名稱為 Org1 和 Org2 的 Peer Org 簽名') .example('bdk chaincode invoke --channel-id fabcar --chaincode-name fabcar --chaincode-function CreateCar -a CAR_ORG1_PEER0 -a BMW -a X6 -a blue -a Org1 --orderer orderer0.example.com:7050 --peer-addresses peer0.org1.example.com:7051 --peer-addresses peer0.org2.example.com:8051', '使用 orderer0.example.com:7050 名稱為 fabcar 的 Chaincode 執行 CreateCar 的 function,需要 CAR_ORG1_PEER0、BMW、X6、blue、Org1 的參數和名稱為 Org1 和 Org2 的 Peer Org 簽名') .option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' }) @@ -33,8 +34,8 @@ export const builder = (yargs: Argv) => { .option('is-init', { type: 'boolean', description: '是否要初始化 Chaincode', alias: 'I', default: false }) .option('chaincode-function', { type: 'string', description: '執行 Chaincode 的 function', alias: 'f' }) .option('args', { type: 'array', description: '執行 Chaincode 需要的參數', alias: 'a' }) - .option('orderer', { type: 'string', description: '選擇 Orderer 執行 Chaincode' }) - .option('peer-addresses', { type: 'array', description: '需要簽名的 Peer address' }) + .option('orderer', { type: 'string', description: '選擇 Orderer 執行 Chaincode (若未輸入則使用discover)' }) + .option('peer-addresses', { type: 'array', description: '需要簽名的 Peer address (若未輸入則使用discover)' }) } export const handler = async (argv: Arguments) => { @@ -43,7 +44,7 @@ export const handler = async (argv: Arguments) => { const chaincode = new Chaincode(config) const channel = new Channel(config) - let invokeChannelInput: ChaincodeInvokeType + let invokeChannelInput: ChaincodeInvokeType | ChaincodeInvokeWithoutDiscoverType if (argv.interactive) { const { channelId } = await prompts([ { @@ -54,9 +55,7 @@ export const handler = async (argv: Arguments) => { }, ], { onCancel }) - const channelGroup = await channel.getChannelGroup(channelId) - - const { chaincodeName, chaincodeFunction, args, isInit, orderer, peerAddresses } = await prompts([ + const { chaincodeName, chaincodeFunction, args, isInit } = await prompts([ { type: 'select', name: 'chaincodeName', @@ -89,27 +88,75 @@ export const handler = async (argv: Arguments) => { ], initial: 1, }, + ]) + const discoverOrderer = await prompts([ { type: 'select', - name: 'orderer', - message: 'Ordering service endpoint', - choices: channelGroup.orderer.map(x => ({ - title: x, - value: x, - })), + name: 'discoverOrderer', + message: 'Set orderer with discover?', + choices: [ + { + title: 'Yes', + value: true, + }, + { + title: 'No', + value: false, + }, + ], }, + ], { onCancel }) + + invokeChannelInput = { channelId, chaincodeName, chaincodeFunction, args, isInit } + + if (discoverOrderer) { + const channelGroup = await channel.getChannelGroup(channelId) + const { orderer } = await prompts([ + { + type: 'select', + name: 'orderer', + message: 'Ordering service endpoint', + choices: channelGroup.orderer.map(x => ({ + title: x, + value: x, + })), + }, + ], { onCancel }) + invokeChannelInput = { ...invokeChannelInput, orderer } + } + + const discoverPeer = await prompts([ { - type: 'multiselect', - name: 'peerAddresses', - message: 'The addresses of the peers to connect to', - choices: channelGroup.anchorPeer.map(x => ({ - title: x, - value: x, - })), + type: 'select', + name: 'discoverPeer', + message: 'Set peers with discover?', + choices: [ + { + title: 'Yes', + value: true, + }, + { + title: 'No', + value: false, + }, + ], }, - ]) - - invokeChannelInput = { channelId, chaincodeName, chaincodeFunction, args, isInit, orderer, peerAddresses } + ], { onCancel }) + if (discoverPeer) { + const channelGroup = await channel.getChannelGroup(channelId) + const { peerAddresses } = await prompts([ + { + type: 'multiselect', + name: 'peerAddresses', + message: 'The addresses of the peers to connect to', + choices: channelGroup.anchorPeer.map(x => ({ + title: x, + value: x, + })), + }, + ], { onCancel }) + invokeChannelInput = { ...invokeChannelInput, peerAddresses } + } } else { invokeChannelInput = { channelId: argv.channelId, @@ -117,8 +164,12 @@ export const handler = async (argv: Arguments) => { chaincodeFunction: argv.chaincodeFunction, args: argv.args || [], isInit: argv.isInit, - orderer: argv.orderer, - peerAddresses: argv.peerAddresses, + } + if (argv.orderer) { + invokeChannelInput = { ...invokeChannelInput, orderer: argv.orderer } + } + if (argv.peerAddresses) { + invokeChannelInput = { ...invokeChannelInput, peerAddresses: argv.peerAddresses } } } diff --git a/src/command/chaincode/package.ts b/src/command/chaincode/package.ts index db64f318..23952f39 100644 --- a/src/command/chaincode/package.ts +++ b/src/command/chaincode/package.ts @@ -27,7 +27,7 @@ export const builder = (yargs: Argv) => { } export const handler = async (argv: Arguments) => { - logger.debug('exec chaincode deploy') + logger.debug('exec chaincode package') const chaincode = new Chaincode(config) diff --git a/src/index.ts b/src/index.ts index 9ee7a6b0..b4b46763 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import Ca from './service/caService' import Chaincode from './service/chaincode' import Channel from './service/channel' import Config from './service/config' +import Discover from './service/discover' import Explorer from './service/explorer' import Network from './service/network' import Orderer from './service/orderer' @@ -13,6 +14,7 @@ export { Chaincode, Channel, Config, + Discover, Explorer, Network, Orderer, @@ -23,11 +25,12 @@ export { export { AbstractService, ParserType } from './service/Service.abstract' export { CaUpType, CaDownType, CaIntermediateType, CaCsrType, CaEnrollCommandTypeEnum, CaEnrollTypeEnum, CaRegisterTypeEnum, CaEnrollType, CaRegisterType, CaBasicType, CaCryptoType, CaSigningType } from './model/type/caService.type' -export { ChaincodePackageType, ChaincodeApproveType, ChaincodeCommitType, ChaincodeDeployType, ChaincodeQueryType, ChaincodeInvokeType, ChaincodeInstallType } from './model/type/chaincode.type' +export { ChaincodePackageType, ChaincodeApproveType, ChaincodeApproveWithoutDiscoverType, ChaincodeApproveStepApproveOnInstanceType, ChaincodeCommitType, ChaincodeQueryType, ChaincodeInvokeType, ChaincodeInstallType, ChaincodeInstallStepSavePackageIdType } from './model/type/chaincode.type' export { PolicyTypeEnum, PolicyStyleEnum, ChannelPolicyType, ChannelCreateType, ChannelJoinType, ChannelUpdateAnchorPeerType, ConfigtxlatorEnum, ChannelCreateChannelConfigComputeType, ChannelCreateChannelConfigSignType, ChannelCreateChannelConfigUpdateType, ChannelConfigEnum, ChannelFetchBlockType, ChannelApproveType, ChannelUpdateType, DecodeEnvelopeType, EnvelopeTypeEnum, EnvelopeVerifyEnum as EnvelopeVerifyENum, DecodeEnvelopeReturnType } from './model/type/channel.type' export { ConfigEnvType, ConfigSetType } from './model/type/config.type' +export { DiscoverPeersType, DiscoverChannelConfigType, DiscoverChaincodeEndorsersType, DiscoverPeersResultType, DiscoverChannelConfigResultType, DiscoverChaincodeEndorsersResultType } from './model/type/discover.type' export { DockerHostConfigType, DockerCreateOptionsType, DockerStartOptionsType, DockerRunCommandType } from './model/type/docker.type' -export { ExplorerUpForMyOrgType, ExplorerUpdateForMyOrgType, ExplorerChannelType } from './model/type/explorer.type' +export { ExplorerUpForMyOrgType, ExplorerUpForMyOrgStepUpType, ExplorerUpdateForMyOrgStepRestartType, ExplorerChannelType } from './model/type/explorer.type' export { NetworkCreateType, NetworkCryptoConfigOrdererOrgType, NetworkCryptoConfigPeerOrgType, NetworkCreateOrdererOrgType, NetworkCreatePeerOrgType, NetworkOrdererPortType, NetworkPeerPortType } from './model/type/network.type' export { OrgPeerOrgNameAndDomainType, OrgJsonType, OrgOrdererCreateType, OrgPeerCreateType } from './model/type/org.type' export { PeerUpType, PeerDownType, PeerAddType, PeerAddOrgToChannelType, PeerAddOrgToSystemChannelType } from './model/type/peer.type' diff --git a/src/instance/bdkFile.ts b/src/instance/bdkFile.ts index aca95543..749b4a7c 100644 --- a/src/instance/bdkFile.ts +++ b/src/instance/bdkFile.ts @@ -205,9 +205,13 @@ export default class BdkFile { return YAML.load(fs.readFileSync(this.getDockerComposeYamlPath(hostName, type)).toString()) as DockerComposeYamlInterface } - public createOrgConfigEnv (address: string, dotEnv: string) { + public createOrgConfigEnv (filename: string, dotEnv: string) { fs.mkdirSync(`${this.bdkPath}/env`, { recursive: true }) - fs.writeFileSync(`${this.bdkPath}/env/${address}.env`, dotEnv) + fs.writeFileSync(`${this.bdkPath}/env/${filename}.env`, dotEnv) + } + + public getOrgConfigEnv (filename: string) { + return parse(fs.readFileSync(`${this.bdkPath}/env/${filename}.env`).toString()) } private createChannelConfigtxFolder (channelName: string) { @@ -446,7 +450,7 @@ export default class BdkFile { } } - private newestFileInFolder (folderName: string) { + public static newestFileName (folderName: string) { const files: string[] = fs.readdirSync(folderName) const newest = { fileName: 'stub', created: new Date('December 31, 1999 00:00:00') } files.forEach(function (file) { @@ -456,7 +460,11 @@ export default class BdkFile { newest.created = stats.ctime } }) - return `${folderName}/${newest.fileName}` + return newest.fileName + } + + private newestFileInFolder (folderName: string) { + return `${folderName}/${BdkFile.newestFileName(folderName)}` } private createPackageIdFolder () { @@ -483,10 +491,18 @@ export default class BdkFile { return fs.readFileSync(this.newestFileInFolder(`${this.bdkPath}/peerOrganizations/${domain}/users/Admin@${domain}/msp/keystore`)).toString() } + public getAdminPrivateKeyFilename (domain: string): string { + return BdkFile.newestFileName(`${this.bdkPath}/peerOrganizations/${domain}/users/Admin@${domain}/msp/keystore`) + } + public getAdminSignCert (domain: string): string { return fs.readFileSync(this.newestFileInFolder(`${this.bdkPath}/peerOrganizations/${domain}/users/Admin@${domain}/msp/signcerts`)).toString() } + public getAdminSignCertFilename (domain: string): string { + return BdkFile.newestFileName(`${this.bdkPath}/peerOrganizations/${domain}/users/Admin@${domain}/msp/signcerts`) + } + public getChannelJson (channel: string, filename: string): string { return fs.readFileSync(`${this.bdkPath}/channel-artifacts/${channel}/${filename}.json`).toString() } diff --git a/src/instance/fabricTools.ts b/src/instance/fabricTools.ts index 462e1f1a..bdc53e71 100644 --- a/src/instance/fabricTools.ts +++ b/src/instance/fabricTools.ts @@ -1,6 +1,6 @@ import { ConfigtxlatorEnum } from '../model/type/channel.type' import { logger } from '../util' -import { DockerResultType } from './infra/InfraRunner.interface' +import { DockerResultType, InfraRunnerResultType } from './infra/InfraRunner.interface' import { AbstractInstance } from './Instance.abstract' interface OptionsType { @@ -155,4 +155,53 @@ export default class FabricTools extends AbstractInstance { [`${chaincodePath}:/chaincode`], options) } + + public async discoverPeers (channel: string, options?: OptionsType): Promise { + const envFile = this.bdkFile.getOrgConfigEnv(`peer-${this.config.hostname}.${this.config.orgDomainName}`) + return await this.infraRunCommand([ + 'discover', 'peers', + '--peerTLSCA', envFile.CORE_PEER_TLS_ROOTCERT_FILE, + '--userKey', `${envFile.CORE_PEER_MSPCONFIGPATH}/keystore/${this.bdkFile.getAdminPrivateKeyFilename(this.config.orgDomainName)}`, + '--userCert', `${envFile.CORE_PEER_MSPCONFIGPATH}/signcerts/${this.bdkFile.getAdminSignCertFilename(this.config.orgDomainName)}`, + '--MSP', envFile.CORE_PEER_LOCALMSPID, + '--server', envFile.CORE_PEER_ADDRESS, + '--channel', channel, + ], + undefined, + undefined, + { ...options, network: this.config.networkName }) + } + + public async discoverChannelConfig (channel: string, options?: OptionsType): Promise { + const envFile = this.bdkFile.getOrgConfigEnv(`peer-${this.config.hostname}.${this.config.orgDomainName}`) + return await this.infraRunCommand([ + 'discover', 'config', + '--peerTLSCA', envFile.CORE_PEER_TLS_ROOTCERT_FILE, + '--userKey', `${envFile.CORE_PEER_MSPCONFIGPATH}/keystore/${this.bdkFile.getAdminPrivateKeyFilename(this.config.orgDomainName)}`, + '--userCert', `${envFile.CORE_PEER_MSPCONFIGPATH}/signcerts/${this.bdkFile.getAdminSignCertFilename(this.config.orgDomainName)}`, + '--MSP', envFile.CORE_PEER_LOCALMSPID, + '--server', envFile.CORE_PEER_ADDRESS, + '--channel', channel, + ], + undefined, + undefined, + { ...options, network: this.config.networkName }) + } + + public async discoverChaincodeEndorsers (channel: string, chaincode: string, options?: OptionsType): Promise { + const envFile = this.bdkFile.getOrgConfigEnv(`peer-${this.config.hostname}.${this.config.orgDomainName}`) + return await this.infraRunCommand([ + 'discover', 'endorsers', + '--peerTLSCA', envFile.CORE_PEER_TLS_ROOTCERT_FILE, + '--userKey', `${envFile.CORE_PEER_MSPCONFIGPATH}/keystore/${this.bdkFile.getAdminPrivateKeyFilename(this.config.orgDomainName)}`, + '--userCert', `${envFile.CORE_PEER_MSPCONFIGPATH}/signcerts/${this.bdkFile.getAdminSignCertFilename(this.config.orgDomainName)}`, + '--MSP', envFile.CORE_PEER_LOCALMSPID, + '--server', envFile.CORE_PEER_ADDRESS, + '--channel', channel, + '--chaincode', chaincode, + ], + undefined, + undefined, + { ...options, network: this.config.networkName }) + } } diff --git a/src/instance/infra/docker/runner.ts b/src/instance/infra/docker/runner.ts index 43e9aaa7..7bf04765 100644 --- a/src/instance/infra/docker/runner.ts +++ b/src/instance/infra/docker/runner.ts @@ -1,6 +1,6 @@ import fs from 'fs' +import stream from 'stream' import YAML from 'js-yaml' -import { WritableStream } from 'memory-streams' import { spawnSync } from 'child_process' import Dockerode from 'dockerode' import { logger } from '../../../util/logger' @@ -52,19 +52,25 @@ export class Runner implements InfraRunner { logger.silly(`docker command: \ndocker run ${createOptions.HostConfig?.AutoRemove ? '--rm ' : ''} -u ${config.UID}:${config.GID} ${createOptions.HostConfig?.NetworkMode ? `--network ${createOptions.HostConfig?.NetworkMode} ` : ''}${(createOptions.HostConfig?.Binds || []).map(x => `-v ${x} `).join('')}${(createOptions.Env || []).map(x => `--env ${x} `).join('')}${image}:${tag || 'latest'} ${commands.join(' ')}`) const startOptions: DockerStartOptionsType = payload.startOptions || {} as DockerStartOptionsType try { - const stdout = new WritableStream() + let stdout = '' + const stdoutStream = new stream.Writable({ + write: function (chunk, encoding, callback) { + stdout += chunk.toString() + callback() + }, + }) const dockerRunResult = await this.dockerode.run( `${image}:${tag || 'latest'}`, commands, - stdout, + stdoutStream, createOptions, startOptions) - logger.silly(`run command output: \n${stdout.toString()}`) + logger.silly(`run command output: \n${stdout}`) logger.debug(`docker run\n image: ${image}\n commands: ${commands.join(' ')}`) if (dockerRunResult[0].StatusCode !== 0) { - throw new FabricContainerError(`[x] [in-docker-container error] ${stdout.toString().split('\r\n').filter(x => x.match(/error/i))}`, stdout.toString()) + throw new FabricContainerError(`[x] [in-docker-container error] ${stdout.split('\r\n').filter(x => x.match(/error/i))}`, stdout) } - return { statusCode: dockerRunResult[0].StatusCode, stdout: stdout.toString() } + return { statusCode: dockerRunResult[0].StatusCode, stdout } } catch (e: any) { if (e instanceof FabricContainerError) { throw e } throw new DockerError(`[x] command [docker run]:${e.message}`) diff --git a/src/model/type/chaincode.type.ts b/src/model/type/chaincode.type.ts index a9cae20d..0852e276 100644 --- a/src/model/type/chaincode.type.ts +++ b/src/model/type/chaincode.type.ts @@ -14,23 +14,35 @@ export interface ChaincodePackageType { * @requires chaincodeName - [string] chaincode 的名稱 * @requires chaincodeVersion - [number] chaincode 的版本號碼 * @requires initRequired - [boolean] 使否需要初始化 - * @requires orderer - [string] orderer 的 address 和 port */ export interface ChaincodeApproveType { channelId: string chaincodeName: string chaincodeVersion: number initRequired: boolean +} + +export type ChaincodeApproveWithoutDiscoverType = ChaincodeApproveType & { orderer: string } + +/** + * @requires orderer - [string] orderer 的 address 和 port + */ +export interface ChaincodeApproveStepApproveOnInstanceType extends ChaincodeApproveType { orderer: string } /** * @requires chaincodeLabel - [string] chaincode 的名稱 - * @requires packageId - [string] chaincode packate 的 id */ export interface ChaincodeInstallType { chaincodeLabel: string - packageId?: string +} + +/** + * @ignore + */ +export interface ChaincodeInstallStepSavePackageIdType extends ChaincodeInstallType { + packageId: string } /** @@ -38,36 +50,23 @@ export interface ChaincodeInstallType { * @requires chaincodeName - [string] chaincode 的名稱 * @requires chaincodeVersion - [number] chaincode 的版本號碼 * @requires initRequired - [boolean] 使否需要初始化 - * @requires orderer - [string] orderer 的 address 和 port - * @requires peerAddresses - [string array] peer address 和 port 的 array */ export interface ChaincodeCommitType { channelId: string chaincodeName: string chaincodeVersion: number initRequired: boolean - orderer: string - peerAddresses: string[] } +export type ChaincodeCommitWithoutDiscoverType = ChaincodeApproveType & ({ orderer: string } | { peerAddresses: string[] }) + /** - * @requires channelId - [string] channel 的名稱 - * @requires label - [string] chaincode 的標籤名稱 - * @requires approve - [boolean] 是否只做到同意的步驟 - * @requires commit - [boolean] 是否只做到發布的步驟 - * @requires initRequired - [boolean] 是否需要初始化 chaincode * @requires orderer - [string] orderer 的 address 和 port * @requires peerAddresses - [string array] peer address 和 port 的 array */ -export interface ChaincodeDeployType { - channelId: string - chaincodeName: string - chaincodeVersion: number - approve: boolean - commit: boolean - initRequired: boolean +export interface ChaincodeCommitStepCommitOnInstanceType extends ChaincodeApproveType { orderer: string - peerAddresses?: string[] + peerAddresses: string[] } /** @@ -85,11 +84,18 @@ export interface ChaincodeQueryType { /** * @requires isInit - [boolean] 是否要初始化 chaincode - * @requires orderer - [string] orderer 的 address 和 port - * @requires peerAddresses - [string array] peer address 和 port 的 array */ export interface ChaincodeInvokeType extends ChaincodeQueryType { isInit: boolean +} + +export type ChaincodeInvokeWithoutDiscoverType = ChaincodeInvokeType & ({ orderer: string } | { peerAddresses: string[] }) + +/** + * @requires orderer - [string] orderer 的 address 和 port + * @requires peerAddresses - [string array] peer address 和 port 的 array + */ +export interface ChaincodeInvokeStepInvokeOnInstanceType extends ChaincodeInvokeType { orderer: string peerAddresses: string[] } diff --git a/src/model/type/discover.type.ts b/src/model/type/discover.type.ts new file mode 100644 index 00000000..dc097fc2 --- /dev/null +++ b/src/model/type/discover.type.ts @@ -0,0 +1,84 @@ +/** + * @requires channel - [string] channel 名稱 + * @requires chaincode - [string] chaincode 名稱 + */ +export interface DiscoverPeersType { + channel: string +} + +/** + * @requires channel - [string] channel 名稱 + */ +export interface DiscoverChannelConfigType { + channel: string +} + +/** + * @requires channel - [string] channel 名稱 + * @requires chaincode - [string] chaincode 名稱 + */ +export interface DiscoverChaincodeEndorsersType { + channel: string + chaincode: string +} + +interface OuIdentifierType { + certificate: string + organizational_unit_identifier: string +} + +interface OrdererEndpointType { + host: string + port: number +} + +export type DiscoverPeersResultType = { + MSPID: string + LedgerHeight: number + Endpoint: string + Identity: string + Chaincodes: string[] +}[] + +export interface DiscoverChannelConfigResultType { + msps: { + [msp: string]: { + name: string + root_certs: string[] + crypto_config: { + signature_hash_family: string + identity_identifier_hash_function: string + } + tls_root_certs: string[] + fabric_node_ous: { + enable: boolean + client_ou_identifier: OuIdentifierType + peer_ou_identifier: OuIdentifierType + admin_ou_identifier: OuIdentifierType + orderer_ou_identifier: OuIdentifierType + } + } + } + orderers: { + [msp: string]: { + endpoint: OrdererEndpointType[] + } + } +} + +export type DiscoverChaincodeEndorsersResultType = { + Chaincode: string + EndorsersByGroups: { + [group: string]: { + MSPID: string + LedgerHeight: number + Endpoint: string + Identity: string + }[] + } + Layouts: { + quantities_by_group: { + [group: string]: number + } + }[] +}[] diff --git a/src/model/type/explorer.type.ts b/src/model/type/explorer.type.ts index c010e232..75a33807 100644 --- a/src/model/type/explorer.type.ts +++ b/src/model/type/explorer.type.ts @@ -6,18 +6,23 @@ export interface ExplorerChannelType { * @requires user - explorer 的預設使用者 * @requires pass - explorer 的預設密碼 * @requires port - explorer 的 port - * @requires channels - channel 與 加入此 Channel 的 hostname */ export interface ExplorerUpForMyOrgType{ user: string pass: string port: number - channels?: ExplorerChannelType // {'my_channel': {hostname: 'peer0'}} } /** - * @requires channels - channel 與 加入此 Channel 的 hostname + * @ignore */ -export interface ExplorerUpdateForMyOrgType { - channels?: ExplorerChannelType // {'my_channel': {hostname: 'peer0'}} +export interface ExplorerUpForMyOrgStepUpType extends ExplorerUpForMyOrgType{ + channels: ExplorerChannelType // {'my_channel': {hostname: 'peer0'}} +} + +/** + * @ignore + */ +export interface ExplorerUpdateForMyOrgStepRestartType { + channels: ExplorerChannelType // {'my_channel': {hostname: 'peer0'}} } diff --git a/src/service/chaincode.ts b/src/service/chaincode.ts index a0890b01..a550a6fc 100644 --- a/src/service/chaincode.ts +++ b/src/service/chaincode.ts @@ -1,10 +1,12 @@ import path from 'path' import FabricTools from '../instance/fabricTools' import FabricInstance from '../instance/fabricInstance' -import { ChaincodeApproveType, ChaincodeCommitType, ChaincodeInstallType, ChaincodeInvokeType, ChaincodePackageType, ChaincodeQueryType } from '../model/type/chaincode.type' +import { ChaincodeApproveStepApproveOnInstanceType, ChaincodeApproveType, ChaincodeCommitStepCommitOnInstanceType, ChaincodeApproveWithoutDiscoverType, ChaincodeCommitType, ChaincodeInstallStepSavePackageIdType, ChaincodeInstallType, ChaincodeInvokeType, ChaincodePackageType, ChaincodeQueryType, ChaincodeCommitWithoutDiscoverType, ChaincodeInvokeStepInvokeOnInstanceType, ChaincodeInvokeWithoutDiscoverType } from '../model/type/chaincode.type' import { ParserType, AbstractService } from './Service.abstract' import { logger } from '../util/logger' import { DockerResultType, InfraRunnerResultType } from '../instance/infra/InfraRunner.interface' +import Discover from './discover' +import { randomFromArray } from '../util/utils' interface ChaincodeParser extends ParserType { installToPeer: (result: DockerResultType, options: {chaincodeLabel: string}) => string @@ -12,6 +14,11 @@ interface ChaincodeParser extends ParserType { query: (result: DockerResultType) => any getChaincodePackageId: (result: DockerResultType, options: {chaincodeLabel: string}) => string getCommittedChaincode: (result: DockerResultType) => string[] + approveStepDiscover: (result: DockerResultType) => string + commitStepDiscoverChannelConfig: (result: DockerResultType) => string + commitStepDiscoverPeers: (result: DockerResultType) => string[] + invokeStepDiscoverChannelConfig: (result: DockerResultType) => string + invokeStepDiscoverEndorsers: (result: DockerResultType) => string[] } export default class Chaincode extends AbstractService { @@ -21,6 +28,27 @@ export default class Chaincode extends AbstractService { query: (result) => JSON.parse(result.stdout || '{}'), getChaincodePackageId: (result, options: {chaincodeLabel: string}) => result.stdout.match(RegExp(`(?<=Package ID: )${options.chaincodeLabel}:.*(?=, Label: ${options.chaincodeLabel})`))?.[0] || '', getCommittedChaincode: (result) => result.stdout.match(/(?<=Name: ).*(?=, Version)/g) || [], + approveStepDiscover: (result) => Discover.chooseOneRandomOrderer(Discover.parser.channelConfig(result)), + commitStepDiscoverChannelConfig: (result) => Discover.chooseOneRandomOrderer(Discover.parser.channelConfig(result)), + commitStepDiscoverPeers: (result) => { + const peerDiscoverResult = Discover.parser.peers(result) + const peerList: Map = new Map() + peerDiscoverResult.forEach(peer => { + peerList.has(peer.MSPID) ? peerList.get(peer.MSPID)?.push(peer.Endpoint) : peerList.set(peer.MSPID, [peer.Endpoint]) + }) + const peers: string[] = [] + Array.from(peerList.keys()).forEach(org => { + const peersOfOrg = peerList.get(org) || [] + peers.push(randomFromArray(peersOfOrg)) + }) + return peers + }, + invokeStepDiscoverChannelConfig: (result) => Discover.chooseOneRandomOrderer(Discover.parser.channelConfig(result)), + invokeStepDiscoverEndorsers: (result) => { + const endorserDiscoverResult = Discover.parser.chaincodeEndorsers(result) + const layout = randomFromArray(endorserDiscoverResult[0].Layouts) + return (Object.keys(layout.quantities_by_group).map(group => (randomFromArray(endorserDiscoverResult[0].EndorsersByGroups[group]).Endpoint))) + }, } /** @@ -37,9 +65,53 @@ export default class Chaincode extends AbstractService { * @description 執行 chaincode 上的 function 發送交易 * @returns 執行 chaincode function 的回覆 */ - public async invoke (payload: ChaincodeInvokeType): Promise { - logger.debug('invoke chaincode') - return await (new FabricInstance(this.config, this.infra)).invokeChaincode(payload.channelId, payload.chaincodeName, payload.chaincodeFunction, payload.args, payload.isInit, payload.orderer, payload.peerAddresses) + public async invoke (payload: ChaincodeInvokeType | ChaincodeInvokeWithoutDiscoverType): Promise { + let orderer: string + let peerAddresses: string[] + + if ('orderer' in payload) { + orderer = payload.orderer + } else { + const discoverChannelConfigResult = await this.invokeSteps().discoverChannelConfig(payload) + if (!('stdout' in discoverChannelConfigResult)) { + logger.error('this service only for docker infra') + throw new Error('this service for docker infra') + } + orderer = Chaincode.parser.commitStepDiscoverChannelConfig(discoverChannelConfigResult) + } + + if ('peerAddresses' in payload) { + peerAddresses = payload.peerAddresses + } else { + const discoverEndorsersResult = await this.invokeSteps().discoverEndorsers(payload) + if (!('stdout' in discoverEndorsersResult)) { + logger.error('this service only for docker infra') + throw new Error('this service for docker infra') + } + peerAddresses = Chaincode.parser.invokeStepDiscoverEndorsers(discoverEndorsersResult) + } + + return await this.invokeSteps().invokeOnInstance({ ...payload, orderer, peerAddresses }) + } + + /** + * @ignore + */ + public invokeSteps () { + return { + discoverEndorsers: async (payload: ChaincodeInvokeType): Promise => { + logger.debug('invoke chaincode step 1 (discover endorsers)') + return await (new Discover(this.config)).chaincodeEndorsers({ channel: payload.channelId, chaincode: payload.chaincodeName }) + }, + discoverChannelConfig: async (payload: ChaincodeInvokeType): Promise => { + logger.debug('invoke chaincode step 2 (discover channel config)') + return await (new Discover(this.config)).channelConfig({ channel: payload.channelId }) + }, + invokeOnInstance: async (payload: ChaincodeInvokeStepInvokeOnInstanceType): Promise => { + logger.debug('invoke chaincode step 3 (invoke)') + return await (new FabricInstance(this.config, this.infra)).invokeChaincode(payload.channelId, payload.chaincodeName, payload.chaincodeFunction, payload.args, payload.isInit, payload.orderer, payload.peerAddresses) + }, + } } /** @@ -60,8 +132,8 @@ export default class Chaincode extends AbstractService { logger.error('this service only for docker infra') throw new Error('this service for docker infra') } - dto.packageId = Chaincode.parser.installToPeer(installToPeerResult, { chaincodeLabel: dto.chaincodeLabel }) - return this.installSteps().savePackageId(dto) + const packageId = Chaincode.parser.installToPeer(installToPeerResult, { chaincodeLabel: dto.chaincodeLabel }) + return this.installSteps().savePackageId({ ...dto, packageId }) } /** @@ -73,7 +145,7 @@ export default class Chaincode extends AbstractService { logger.debug('install chaincode step 1 (install chaincode)') return await (new FabricInstance(this.config, this.infra)).installChaincode(dto.chaincodeLabel) }, - savePackageId: (dto: ChaincodeInstallType): string => { + savePackageId: (dto: ChaincodeInstallStepSavePackageIdType): string => { logger.debug('install chaincode step 2 (save package id)') this.bdkFile.savePackageId(dto.chaincodeLabel, dto.packageId || '') return dto.packageId || '' @@ -94,18 +166,88 @@ export default class Chaincode extends AbstractService { /** * @description 同意 chaincode */ - public async approve (payload: ChaincodeApproveType): Promise { - logger.debug('approve for my org') - const packageId = this.bdkFile.getPackageId(`${payload.chaincodeName}_${payload.chaincodeVersion}`) - return await (new FabricInstance(this.config, this.infra)).approveChaincode(payload.channelId, payload.chaincodeName, payload.chaincodeVersion, packageId, payload.initRequired, payload.orderer) + public async approve (payload: ChaincodeApproveType | ChaincodeApproveWithoutDiscoverType): Promise { + let orderer: string + if ('orderer' in payload) { + orderer = payload.orderer + } else { + const discoverResult = await this.approveSteps().discover(payload) + if (!('stdout' in discoverResult)) { + logger.error('this service only for docker infra') + throw new Error('this service for docker infra') + } + orderer = Chaincode.parser.approveStepDiscover(discoverResult) + } + return await this.approveSteps().approveOnInstance({ ...payload, orderer }) + } + + /** + * @ignore + */ + public approveSteps () { + return { + discover: async (payload: ChaincodeApproveType): Promise => { + logger.debug('approve chaincode step 1 (discover)') + return await (new Discover(this.config)).channelConfig({ channel: payload.channelId }) + }, + approveOnInstance: async (payload: ChaincodeApproveStepApproveOnInstanceType): Promise => { + logger.debug('approve chaincode step 2 (approve)') + const packageId = this.bdkFile.getPackageId(`${payload.chaincodeName}_${payload.chaincodeVersion}`) + return await (new FabricInstance(this.config, this.infra)).approveChaincode(payload.channelId, payload.chaincodeName, payload.chaincodeVersion, packageId, payload.initRequired, payload.orderer) + }, + } } /** * @description 發布 chaincode */ - public async commit (payload: ChaincodeCommitType): Promise { - logger.debug('Commit chaincode definition') - return await (new FabricInstance(this.config, this.infra)).commitChaincode(payload.channelId, payload.chaincodeName.replace('_', '-'), payload.chaincodeVersion, payload.initRequired, payload.orderer, payload.peerAddresses) + public async commit (payload: ChaincodeCommitType | ChaincodeCommitWithoutDiscoverType): Promise { + let orderer: string + let peerAddresses: string[] + + if ('orderer' in payload) { + orderer = payload.orderer + } else { + const discoverChannelConfigResult = await this.commitSteps().discoverChannelConfig(payload) + if (!('stdout' in discoverChannelConfigResult)) { + logger.error('this service only for docker infra') + throw new Error('this service for docker infra') + } + orderer = Chaincode.parser.commitStepDiscoverChannelConfig(discoverChannelConfigResult) + } + + if ('peerAddresses' in payload) { + peerAddresses = payload.peerAddresses + } else { + const discoverPeersResult = await this.commitSteps().discoverPeers(payload) + if (!('stdout' in discoverPeersResult)) { + logger.error('this service only for docker infra') + throw new Error('this service for docker infra') + } + peerAddresses = Chaincode.parser.commitStepDiscoverPeers(discoverPeersResult) + } + + return await this.commitSteps().commitOnInstance({ ...payload, orderer, peerAddresses }) + } + + /** + * @ignore + */ + public commitSteps () { + return { + discoverPeers: async (payload: ChaincodeCommitType): Promise => { + logger.debug('commit chaincode step 1 (discover peers)') + return await (new Discover(this.config)).peers({ channel: payload.channelId }) + }, + discoverChannelConfig: async (payload: ChaincodeCommitType): Promise => { + logger.debug('commit chaincode step 2 (discover channel config)') + return await (new Discover(this.config)).channelConfig({ channel: payload.channelId }) + }, + commitOnInstance: async (payload: ChaincodeCommitStepCommitOnInstanceType): Promise => { + logger.debug('commit chaincode step 3 (commit)') + return await (new FabricInstance(this.config, this.infra)).commitChaincode(payload.channelId, payload.chaincodeName.replace('_', '-'), payload.chaincodeVersion, payload.initRequired, payload.orderer, payload.peerAddresses) + }, + } } /** diff --git a/src/service/discover.ts b/src/service/discover.ts new file mode 100644 index 00000000..3f19afa5 --- /dev/null +++ b/src/service/discover.ts @@ -0,0 +1,41 @@ +import { ParserType, AbstractService } from './Service.abstract' +import { DiscoverChaincodeEndorsersResultType, DiscoverChaincodeEndorsersType, DiscoverChannelConfigResultType, DiscoverChannelConfigType, DiscoverPeersResultType, DiscoverPeersType } from '../model/type/discover.type' +import FabricTools from '../instance/fabricTools' +import { DockerResultType } from '../instance/infra/InfraRunner.interface' +import { randomFromArray } from '../util/utils' + +interface DiscoverParser extends ParserType { + peers: (dockerResult: DockerResultType) => DiscoverPeersResultType + channelConfig: (dockerResult: DockerResultType) => DiscoverChannelConfigResultType + chaincodeEndorsers: (dockerResult: DockerResultType) => DiscoverChaincodeEndorsersResultType +} + +export default class Discover extends AbstractService { + static readonly parser: DiscoverParser = { + peers: (result) => JSON.parse(result.stdout), + channelConfig: (result) => JSON.parse(result.stdout), + chaincodeEndorsers: (result) => JSON.parse(result.stdout), + } + + public async peers (payload: DiscoverPeersType) { + return await (new FabricTools(this.config, this.infra)).discoverPeers(payload.channel) + } + + public async channelConfig (payload: DiscoverChannelConfigType) { + return await (new FabricTools(this.config, this.infra)).discoverChannelConfig(payload.channel) + } + + public async chaincodeEndorsers (payload: DiscoverChaincodeEndorsersType) { + return await (new FabricTools(this.config, this.infra)).discoverChaincodeEndorsers(payload.channel, payload.chaincode) + } + + public static chooseOneRandomOrderer (result: DiscoverChannelConfigResultType): string { + const orderers: string[] = [] + Object.values(result.orderers).forEach((ordererOrg) => { + ordererOrg.endpoint.forEach(orderer => { + orderers.push(`${orderer.host}:${orderer.port}`) + }) + }) + return randomFromArray(orderers) // choose a random orderer + } +} diff --git a/src/service/explorer.ts b/src/service/explorer.ts index e0fbe8ec..02a59ee8 100644 --- a/src/service/explorer.ts +++ b/src/service/explorer.ts @@ -1,5 +1,5 @@ import { logger } from '../util/logger' -import { ExplorerChannelType, ExplorerUpdateForMyOrgType, ExplorerUpForMyOrgType } from '../model/type/explorer.type' +import { ExplorerChannelType, ExplorerUpdateForMyOrgStepRestartType, ExplorerUpForMyOrgStepUpType, ExplorerUpForMyOrgType } from '../model/type/explorer.type' import ExplorerConnectionProfileYaml from '../model/yaml/explorer/explorerConnectionProfileYaml' import ExplorerConfigYaml from '../model/yaml/explorer/explorerConfigYaml' import Channel from './channel' @@ -26,7 +26,7 @@ export default class Explorer extends AbstractService { } /** @ignore */ - private createExplorerConfig (data: ExplorerUpForMyOrgType | ExplorerUpdateForMyOrgType) { + private createExplorerConfig (data: ExplorerUpForMyOrgStepUpType | ExplorerUpdateForMyOrgStepRestartType) { logger.debug(`Create file: ${this.config.networkName}.json`) const explorerConnectionProfileYaml = new ExplorerConnectionProfileYaml() explorerConnectionProfileYaml.setName(this.config.networkName) @@ -88,7 +88,7 @@ export default class Explorer extends AbstractService { logger.debug('up explorer for my org step 1 (fetch joined channel)') return await (new Channel(this.config)).listJoinedChannel() }, - up: async (payload: ExplorerUpForMyOrgType): Promise => { + up: async (payload: ExplorerUpForMyOrgStepUpType): Promise => { logger.debug('up explorer for my org step 2 (start explorer)') this.createExplorerConfig(payload) const dockerComposeYaml = new ExplorerDockerComposeYaml(this.config, payload.port) @@ -121,9 +121,9 @@ export default class Explorer extends AbstractService { logger.debug('update explorer for my org step 1 (fetch joined channel)') return await (new Channel(this.config, this.infra)).listJoinedChannel() }, - restart: async (payload: ExplorerUpdateForMyOrgType): Promise => { + restart: async (payload: ExplorerUpdateForMyOrgStepRestartType): Promise => { logger.debug('update explorer for my org step 2 (restart explorer)') - this.createExplorerConfig(payload.channels || {}) + this.createExplorerConfig(payload) return await (new ExplorerInstance(this.config, this.infra)).restart() }, } diff --git a/src/util/utils.ts b/src/util/utils.ts index 09e94910..3f5f02f9 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -21,6 +21,7 @@ export function iterateFormat ( return item } +export const randomFromArray = (x: Array) => x[Math.floor(Math.random() * x.length)] // export function iterateObject2 (obj: Map, callback: (x: string) => any) { // for (const key in obj) { // if (typeof obj[key] === 'object') {