Skip to content

Commit

Permalink
add new command bdk channel decode-envelop
Browse files Browse the repository at this point in the history
  • Loading branch information
kth-tw committed Dec 28, 2021
1 parent 26724bc commit b4d5530
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 31 deletions.
44 changes: 22 additions & 22 deletions cicd/test_script/steps/add-new-orderer-org.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ bdk org config import -f ./cicd/test_script/tmp/export-new-orderer.json
# [org0 orderer] add new orderer into system channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk org orderer add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel" -n ${ORDERER_ORG_NAME_ORGNEW}
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel"
Expand All @@ -34,19 +34,19 @@ sleep 20
# [orgnew orderer] add orderer0 into system channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk orderer consenter add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel" -n ${ORDERER_ORG_NAME_ORGNEW} -h ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [orgnew orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel"
Expand All @@ -59,19 +59,19 @@ sleep 20
# [orgnew orderer] add orderer1 into system channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER1}
bdk orderer consenter add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel" -n ${ORDERER_ORG_NAME_ORGNEW} -h ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER1}
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [orgnew orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c "system-channel"
Expand All @@ -84,35 +84,35 @@ sleep 20
# [org0 orderer] add new orderer into application channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk org orderer add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME} -n ${ORDERER_ORG_NAME_ORGNEW}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME}

# [orgnew orderer] add orderer0 into application channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk orderer consenter add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME} -n ${ORDERER_ORG_NAME_ORGNEW} -h ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [orgnew orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME}
Expand All @@ -125,19 +125,19 @@ sleep 20
# [orgnew orderer] add orderer1 into application channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER1}
bdk orderer consenter add -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME} -n ${ORDERER_ORG_NAME_ORGNEW} -h ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER1}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [orgnew orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORGNEW} ${ORDERER_ORG_DOMAIN_ORGNEW} ${ORDERER_ORG_HOSTNAME_ORGNEW_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}
# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel update -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -c ${CHANNEL_NAME}
Expand Down
14 changes: 7 additions & 7 deletions cicd/test_script/steps/add-new-peer-org.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ bdk org config import -f ./cicd/test_script/tmp/export-new-peer.json
# [org0 orderer] add orgnew into system-channel
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk org peer add-system-channel -o ${ORDERER_ORG_URL_ORG0_ORDERER0} -n ${PEER_ORG_NAME_ORGNEW}
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"

# [org0 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG0} ${ORDERER_ORG_DOMAIN_ORG0} ${ORDERER_ORG_HOSTNAME_ORG0_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"

# [org1 orderer] approve
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
bdk channel approve -c "system-channel"
# bdk channel decode-envelope -c "system-channel"
bdk channel decode-envelope -c "system-channel"

# [org1 orderer] update
export_env 'orderer' ${ORDERER_ORG_NAME_ORG1} ${ORDERER_ORG_DOMAIN_ORG1} ${ORDERER_ORG_HOSTNAME_ORG1_ORDERER0}
Expand All @@ -31,22 +31,22 @@ sleep 2
# [org0] add orgnew in
export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer0'
bdk org peer add -c ${CHANNEL_NAME} -n ${PEER_ORG_NAME_ORGNEW}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}

# [org0] approve
export_env 'peer' ${PEER_ORG_NAME_ORG0} ${PEER_ORG_DOMAIN_ORG0} 'peer0'
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}

# [org1] approve
export_env 'peer' ${PEER_ORG_NAME_ORG1} ${PEER_ORG_DOMAIN_ORG1} 'peer0'
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}

# [org2] approve
export_env 'peer' ${PEER_ORG_NAME_ORG2} ${PEER_ORG_DOMAIN_ORG2} 'peer0'
bdk channel approve -c ${CHANNEL_NAME}
# bdk channel decode-envelope -c ${CHANNEL_NAME}
bdk channel decode-envelope -c ${CHANNEL_NAME}

# [org2] update
export_env 'peer' ${PEER_ORG_NAME_ORG2} ${PEER_ORG_DOMAIN_ORG2} 'peer0'
Expand Down
11 changes: 11 additions & 0 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,17 @@ Description: 更新 Channel 的設定檔
| -o, --orderer | string | 選擇使用的 Orderer | |
| -c, --channel-name | string | Channel 的名稱 | |

### `bdk channel decode-envelope`

Description: 解析 Approve 或 Update 的信封內容

| Options | Type | Description | Required | Default |
| ------------------ | :-----: | ------------------------------ | :------: | ------- |
| --help | boolean | Show help | |
| --version | boolean | Show version number | |
| -i, --interactive | boolean | 是否使用 Cathay BDK 互動式問答 | |
| -c, --channel-name | string | Channel 的名稱 | |

## Config

### `bdk config init`
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"doc:create": "npx typedoc --readme none --out ./api-docs --packages ."
},
"dependencies": {
"deep-object-diff": "^1.1.0",
"dockerode": "^3.3.1",
"dotenv": "^8.2.0",
"envfile": "^6.14.0",
Expand Down
72 changes: 72 additions & 0 deletions src/command/channel/decode-envelope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import prompts from 'prompts'
import { Arguments, Argv } from 'yargs'
import { logger, onCancel, ParamsError } from '../../util'
import config from '../../config'
import Channel from '../../service/channel'
import { getChannelEnvelopeList } from '../../model/prompts/util'
import { EnvelopeTypeEnum } from '../../model/type/channel.type'

export const command = 'decode-envelope'

export const desc = '解析 Approve 或 Update 的信封內容'

interface OptType {
interactive: boolean
channelName: string
}

const channelEnvelopeList = getChannelEnvelopeList(config)

export const builder = (yargs: Argv<OptType>) => {
return yargs
.example('bdk channel decode-envelope --interactive', 'Cathay BDK 互動式問答')
.example('bdk channel decode-envelope --channel-name test', '解析 channel test 的更動資訊')
.option('interactive', { type: 'boolean', description: '是否使用 Cathay BDK 互動式問答', alias: 'i' })
.option('channel-name', { type: 'string', choices: channelEnvelopeList, description: 'Channel 的名稱', alias: 'c' })
}

export const handler = async (argv: Arguments<OptType>) => {
const channel = new Channel(config)

const channelName: string = await (async () => {
if (argv.interactive) {
return (await prompts([
{
type: 'select',
name: 'channelName',
message: 'What is your channel name?',
choices: channelEnvelopeList.map(x => ({
title: x,
value: x,
})),
},
], { onCancel })).channelName
} else if (argv.channelName) {
return argv.channelName
} else {
throw new ParamsError('Invalid params: Required parameter missing <channel-name>')
}
})()

const decodeResult = (await channel.decodeEnvelope({ channelName }))
logger.info(`Approved org: ${decodeResult.approved.toString()}`)
switch (decodeResult.type) {
case EnvelopeTypeEnum.ADD_PEER_TO_SYSTEM_CHANNEL:
case EnvelopeTypeEnum.ADD_PEER_TO_APPLICATION_CHANNEL:
logger.info(`Add peer org "${decodeResult.org}" into channel`)
logger.info(`Verify: ${decodeResult.verify}`)
break
case EnvelopeTypeEnum.ADD_ORDERER_TO_CHANNEL:
logger.info(`Add orderer org "${decodeResult.org}" into channel`)
logger.info(`Verify: ${decodeResult.verify}`)
break
case EnvelopeTypeEnum.UPDATE_ANCHOR_PEER:
logger.info(`Update anchor peer of peer org "${decodeResult.org}": ${decodeResult.anchorPeers}`)
break
case EnvelopeTypeEnum.ADD_ORDERER_CONSENTER:
logger.info(`Update consenter: ${decodeResult.consensus}`)
break
default:
break
}
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ 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 { PolicyTypeEnum, PolicyStyleEnum, ChannelPolicyType, ChannelCreateType, ChannelJoinType, ChannelUpdateAnchorPeerType, ConfigtxlatorEnum, ChannelCreateChannelConfigComputeType, ChannelCreateChannelConfigSignType, ChannelCreateChannelConfigUpdateType, ChannelConfigEnum, ChannelFetchBlockType, ChannelApproveType, ChannelUpdateType } from './model/type/channel.type'
export { PolicyTypeEnum, PolicyStyleEnum, ChannelPolicyType, ChannelCreateType, ChannelJoinType, ChannelUpdateAnchorPeerType, ConfigtxlatorEnum, ChannelCreateChannelConfigComputeType, ChannelCreateChannelConfigSignType, ChannelCreateChannelConfigUpdateType, ChannelConfigEnum, ChannelFetchBlockType, ChannelApproveType, ChannelUpdateType, DecodeEnvelopeType, EnvelopeTypeEnum, EnvelopeVerifyENum, DecodeEnvelopeReturnType } from './model/type/channel.type'
export { ConfigEnvType, ConfigSetType } from './model/type/config.type'
export { DockerHostConfigType, DockerCreateOptionsType, DockerStartOptionsType, DockerRunCommandType } from './model/type/docker.type'
export { ExplorerUpForMyOrgType, ExplorerUpdateForMyOrgType, ExplorerChannelType } from './model/type/explorer.type'
Expand Down
4 changes: 4 additions & 0 deletions src/instance/bdkFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,4 +486,8 @@ export default class BdkFile {
public getAdminSignCert (domain: string): string {
return fs.readFileSync(this.newestFileInFolder(`${this.bdkPath}/peerOrganizations/${domain}/users/Admin@${domain}/msp/signcerts`)).toString()
}

public getChannelJson (channel: string, filename: string): string {
return fs.readFileSync(`${this.bdkPath}/channel-artifacts/${channel}/${filename}.json`).toString()
}
}
30 changes: 30 additions & 0 deletions src/model/type/channel.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,33 @@ export interface ChannelUpdateType {
channelName: string
orderer: string
}

/**
* @requires channelName - [string] channel 的名稱
*/
export interface DecodeEnvelopeType {
channelName: string
}

export enum EnvelopeTypeEnum{
UPDATE_ANCHOR_PEER = 'UPDATE_ANCHOR_PEER',
ADD_PEER_TO_APPLICATION_CHANNEL = 'ADD_PEER_TO_APPLICATION_CHANNEL',
ADD_PEER_TO_SYSTEM_CHANNEL = 'ADD_PEER_TO_SYSTEM_CHANNEL',
ADD_ORDERER_TO_CHANNEL = 'ADD_ORDERER_TO_CHANNEL',
ADD_ORDERER_CONSENTER = 'ADD_ORDERER_CONSENTER',
}

export enum EnvelopeVerifyENum{
VERIFIED = 'VERIFIED',
NOT_MATCH = 'NOT_MATCH',
NO_FILE = 'NO_FILE'
}

export interface DecodeEnvelopeReturnType {
approved: string[]
type: EnvelopeTypeEnum
org?: string
verify?: EnvelopeVerifyENum
anchorPeers? : string[]
consensus?: string[]
}
Loading

0 comments on commit b4d5530

Please sign in to comment.