Skip to content

Commit

Permalink
add discover to bdk chaincode invoke
Browse files Browse the repository at this point in the history
  • Loading branch information
kth-tw committed Jan 17, 2022
1 parent 0b45d31 commit e80093d
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 44 deletions.
2 changes: 1 addition & 1 deletion cicd/test_script/steps/chaincode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ 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'
Expand Down
24 changes: 12 additions & 12 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,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`

Expand Down
101 changes: 76 additions & 25 deletions src/command/chaincode/invoke.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -25,6 +25,7 @@ interface OptType {
export const builder = (yargs: Argv<OptType>) => {
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' })
Expand All @@ -33,8 +34,8 @@ export const builder = (yargs: Argv<OptType>) => {
.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<OptType>) => {
Expand All @@ -43,7 +44,7 @@ export const handler = async (argv: Arguments<OptType>) => {
const chaincode = new Chaincode(config)
const channel = new Channel(config)

let invokeChannelInput: ChaincodeInvokeType
let invokeChannelInput: ChaincodeInvokeType | ChaincodeInvokeWithoutDiscoverType
if (argv.interactive) {
const { channelId } = await prompts([
{
Expand All @@ -54,9 +55,7 @@ export const handler = async (argv: Arguments<OptType>) => {
},
], { 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',
Expand Down Expand Up @@ -89,36 +88,88 @@ export const handler = async (argv: Arguments<OptType>) => {
],
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,
chaincodeName: argv.chaincodeName,
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 }
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/model/type/chaincode.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,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[]
}
61 changes: 57 additions & 4 deletions src/service/chaincode.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import path from 'path'
import FabricTools from '../instance/fabricTools'
import FabricInstance from '../instance/fabricInstance'
import { ChaincodeApproveStepApproveOnInstanceType, ChaincodeApproveType, ChaincodeCommitStepCommitOnInstanceType, ChaincodeApproveWithoutDiscoverType, ChaincodeCommitType, ChaincodeInstallStepSavePackageIdType, ChaincodeInstallType, ChaincodeInvokeType, ChaincodePackageType, ChaincodeQueryType, ChaincodeCommitWithoutDiscoverType } 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
Expand All @@ -16,6 +17,8 @@ interface ChaincodeParser extends ParserType {
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 {
Expand All @@ -40,6 +43,12 @@ export default class Chaincode extends AbstractService {
})
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)))
},
}

/**
Expand All @@ -56,9 +65,53 @@ export default class Chaincode extends AbstractService {
* @description 執行 chaincode 上的 function 發送交易
* @returns 執行 chaincode function 的回覆
*/
public async invoke (payload: ChaincodeInvokeType): Promise<InfraRunnerResultType> {
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<InfraRunnerResultType> {
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<InfraRunnerResultType> => {
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<InfraRunnerResultType> => {
logger.debug('invoke chaincode step 2 (discover channel config)')
return await (new Discover(this.config)).channelConfig({ channel: payload.channelId })
},
invokeOnInstance: async (payload: ChaincodeInvokeStepInvokeOnInstanceType): Promise<InfraRunnerResultType> => {
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)
},
}
}

/**
Expand Down

0 comments on commit e80093d

Please sign in to comment.