diff --git a/hyperledger_fabric/test/channel-update-config.sh b/hyperledger_fabric/test/channel-update-config.sh new file mode 100644 index 00000000..1a22a261 --- /dev/null +++ b/hyperledger_fabric/test/channel-update-config.sh @@ -0,0 +1,335 @@ +#!/usr/bin/env sh + +# Fetch a config block and update it. The new channel cfg section will be generated by $CAL_NEW_CFG_PY + +# Usage: ./script channelListFile ordererURL mspId mspPath +# For testing a single channel, can use as: ./script channel ordererURL mspId mspPath + +SYS_CHANNEL_NAME="testchainid" # default system channel name +CAL_NEW_CFG_PY="calc-new-channel-cfg.py" # script to calculate the new channel cfg section + +# use configtxlator to decode pb to json +# Usage: configtxlatorDecode msgType input output +configtxlatorDecode() { + msgType=$1 + input=$2 + output=$3 + + if [ ! -f "$input" ]; then + echo "configDecode: input file not found" + exit 1 + fi + + if ! command -v configtxlator >/dev/null 2>&1; then + echo "configtxlator could not be found, please install it first" + exit 1 + fi + + echo "Decode $input --> $output using type $msgType" + configtxlator proto_decode \ + --type="${msgType}" \ + --input="${input}" \ + --output="${output}" + + [ $? -ne 0 ] && echo "Failed to decode config section from pb" +} + +# use configtxlator to encode json to pb +# Usage: configtxlatorEncode msgType input output +configtxlatorEncode() { + msgType=$1 + input=$2 + output=$3 + + if [ ! -f "$input" ]; then + echo "configEncode: input file not found" + exit 1 + fi + + if ! command -v configtxlator >/dev/null 2>&1; then + echo "configtxlator could not be found, please install it first" + exit 1 + fi + + echo "Config Encode $input --> $output using type $msgType" + configtxlator proto_encode \ + --type="${msgType}" \ + --input="${input}" \ + --output="${output}" + + [ $? -eq 0 ] || echo "Failed to encode config section to pb" +} + +# compute diff between two pb +# Usage: configtxlatorCompare channel origin_pb updated_pb output_pb +configtxlatorCompare() { + channel=$1 + origin=$2 + updated=$3 + output=$4 + + echo "Config Compare $origin vs $updated > ${output} in channel $channel" + if [ ! -f "$origin" ] || [ ! -f "$updated" ]; then + echo "input file not found" + exit 1 + fi + + configtxlator compute_update \ + --original="${origin}" \ + --updated="${updated}" \ + --channel_id="${channel}" \ + --output="${output}" + + [ $? -eq 0 ] || echo "Failed to compute config update" +} + +# fetchConfigBlock fetch the latest config block, and parse the config section into channelCfgJson +# Usage: fetchConfigBlock channel mspId mspPath ordererURL ordererTLSRoot channelCfgJson +fetchConfigBlock() { + if [ $# -lt 6 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channel channel_config_block_cfg.json update_tx.pb" + exit 1 + fi + + channel=$1 + mspId=$2 + mspPath=$3 + ordererURL=$4 + ordererTLSRoot=$5 + channelCfgJson=$6 + config_block=${channel}_config.block + PAYLOAD_CFG_PATH=".data.data[0].payload.data.config" + + export CORE_PEER_LOCALMSPID=${mspId} + export CORE_PEER_MSPCONFIGPATH=${mspPath} + export CORE_PEER_TLS_ROOTCERT_FILE=${ordererTLSRoot} + + peer channel fetch config "${config_block}" \ + -c "${channel}" \ + -o "${ordererURL}" \ + --tls \ + --cafile "${ordererTLSRoot}" + + echo "[${channel}] Decode config block into ${channel}_config.block.json" + configtxlatorDecode "common.Block" "${channel}_config.block" "${channel}_config_block.json" + + echo "[${channel}] Export the config section ${PAYLOAD_CFG_PATH} from ${channel}_config_block.json into ${channelCfgJson}" + jq "${PAYLOAD_CFG_PATH}" "${channel}_config_block.json" >"${channelCfgJson}" + jq . "${channelCfgJson}" >/dev/null + [ $? -ne 0 ] && { + echo "${channel}_config_block_cfg.json is invalid" + exit 1 + } + + return 0 +} + +# Generate a new config section json from old config section, including the channel cfg updates +# Usage genNewCfgSection channel old_config_json new_config_json +calcNewCfgSection() { + if [ $# -lt 3 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channel channel_config_block_cfg.json channel_config_block_cfg_new.json" + exit 1 + fi + channel=$1 + oldCfgJson=$2 # ${channel}_config_block_cfg.json + newCfgJson=$3 # ${channel}_config_block_cfg_new.json + + # TODO: here we calculate the new channel cfg json using python + if [ "${channel}" = "${SYS_CHANNEL_NAME}" ]; then + python3 $CAL_NEW_CFG_PY --sys --input $oldCfgJson --output $newCfgJson + else + python3 $CAL_NEW_CFG_PY --input $oldCfgJson --output $newCfgJson + fi + + jq . "${newCfgJson}" >/dev/null + [ $? -ne 0 ] && { + echo "${newCfgJson} is invalid" + exit 1 + } +} + +# Generate a channel update transaction based on existing config block +# Usage genUpdateTx channel old_config_json update_tx_pb +genUpdateTx() { + if [ $# -lt 3 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channel channel_config_block_cfg.json update_tx.pb" + exit 1 + fi + channel=$1 + oldCfgJson=$2 # ${channel}_config_block_cfg.json + oldCfgPb=${channel}_config_block_cfg.pb + newCfgJson=${channel}_config_block_cfg_new.json + newCfgPb=${channel}_config_block_cfg_new.pb + deltaCfgPb=${channel}_config_block_cfg_delta.pb + deltaCfgJson=${channel}_config_block_cfg_delta.json + deltaCfgEnvJson=${channel}_config_block_cfg_delta_env.json + deltaCfgEnvPb=${3:-${channel}_config_block_cfg_delta_env.pb} + + echo "[$channel] Start generating a channel update tx..." + + echo "[$channel] Modify channel cfg section: ${oldCfgJson}-->${newCfgJson}" + calcNewCfgSection "${channel}" "${oldCfgJson}" "${newCfgJson}" + + echo "[$channel] Convert channel cfg section json into pb files ..." + configtxlatorEncode "common.Config" "${oldCfgJson}" "${oldCfgPb}" + configtxlatorEncode "common.Config" "${newCfgJson}" "${newCfgPb}" + + echo "[$channel] Calculate the config delta between pb files: ${oldCfgPb}+${newCfgPb}-->${deltaCfgPb}" + configtxlatorCompare "${channel}" "${oldCfgPb}" "${newCfgPb}" "${deltaCfgPb}" + + echo "[$channel] Decode the config delta pb into json: ${deltaCfgPb}-->${deltaCfgJson}" + configtxlatorDecode "common.ConfigUpdate" "${deltaCfgPb}" "${deltaCfgJson}" + jq . "${deltaCfgJson}" >/dev/null + [ $? -ne 0 ] && { + echo "${deltaCfgJson} is invalid" + exit 1 + } + + echo "[$channel] Wrap the delta config section as an envelope: ${deltaCfgJson}-->${deltaCfgEnvJson}" + echo '{"payload":{"header":{"channel_header":{"channel_id":"'"$channel"'", "type":2}},"data":{"config_update":'$(cat ${deltaCfgJson})'}}}' | jq . >"${deltaCfgEnvJson}" + + echo "[$channel] Encode the delta config update envelope into pb: ${deltaCfgEnvJson}-->${deltaCfgPb}" + configtxlatorEncode "common.Envelope" "${deltaCfgEnvJson}" "${deltaCfgEnvPb}" + + echo "[$channel] Channel update tx is generated as ${deltaCfgPb}, now ready to sign and send the channel update tx" + + return 0 +} + +# Sign a channel update transaction +# Usage: signUpdateTx mspId mspPath tx +signUpdateTx() { + if [ $# -lt 3 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) mspId mspPath tx" + fi + + mspId=$1 + mspPath=$2 + tx=$3 + + echo "Sign channel config tx $tx by $mspId" + [ -f "${tx}" ] || { + echo "${tx} not exist" + exit 1 + } + + export CORE_PEER_LOCALMSPID=${mspId} + export CORE_PEER_MSPCONFIGPATH=${mspPath} + + peer channel signconfigtx -f "${tx}" >log.txt 2>&1 + rc=$? + [ $rc -ne 0 ] && cat log.txt + if [ $rc -ne 0 ]; then + echo "Sign channel config tx $tx by $mspId failed" + else + echo "Sign channel config tx $tx by $mspId is successful" + fi + + return 0 +} + +# Send a channel update transaction +# Usage: sendUpdateTx "${channel}" "${mspId}" "${mspPath}" "${ordererURL}" "${ordererTLSRoot}" "${updateTx}" +sendUpdateTx() { + if [ $# -lt 6 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channel mspId mspPath ordererURL ordererTLSRoot updateTx" + exit 1 + fi + + channel=$1 + mspId=$2 + mspPath=$3 + ordererURL=$4 + ordererTLSRoot=$5 + tx=$6 + + export CORE_PEER_LOCALMSPID=${mspId} + export CORE_PEER_MSPCONFIGPATH=${mspPath} + export CORE_PEER_TLS_ROOTCERT_FILE=${ordererTLSRoot} + + echo "[${channel}] Send the channel update tx by $mspId $mspPath to $ordererURL" + peer channel update \ + -c "${channel}" \ + -o "${ordererURL}" \ + -f "${tx}" \ + --tls \ + --cafile "${ordererTLSRoot}" \ + >log.txt 2>&1 + + [ $? -ne 0 ] && { echo "[${channel}] Failed to send channel update tx to OSN" && cat log.txt; } + + return 0 +} + +# Entrance function, will call other functions +# Update a single channel's config, need to be founder's admin +# Usage: updateChannel channel, ordererURL, mspId, mspPath +UpdateChannel() { + if [ $# -lt 3 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channel ordererURL mspId mspPath=${PWD}/msp-mspId" + exit 1 + fi + channel=$1 + ordererURL=$2 + mspId=$3 + mspPath=${4:-${PWD}/msp-${mspId}} # Suppose the msp path named as msp-${msp_id} + ordererTLSRoot=${mspPath}/tlscacerts/tlsca.cert + channelCfgJson=${channel}_config_block_cfg.json + updateTx="${channel}_config_block_cfg_delta_env.pb" + + export FABRIC_LOGGING_SPEC="debug" + #export CORE_PEER_LOCALMSPID=${mspId} + #export CORE_PEER_MSPCONFIGPATH=${mspPath} + #export CORE_PEER_TLS_ROOTCERT_FILE=${ordererTLSRoot} + #export CORE_PEER_TLS_ENABLED=true # Let client use TLS connection when connecting to peer + + echo "[${channel}] Fetch config block from ${ordererURL} and decode the cfg section into ${channelCfgJson}" + fetchConfigBlock "${channel}" "${mspId}" "${mspPath}" "${ordererURL}" "${ordererTLSRoot}" "$channelCfgJson" + [ $? -ne 0 ] && { echo "[${channel}] Failed to fetch latest config from OSN" && exit 1; } + + echo "[${channel}] Generate the channel update tx" + genUpdateTx "${channel}" "${channelCfgJson}" "${updateTx}" + [ $? -ne 0 ] && { echo "[${channel}] Failed to generate new channel config" && exit 1; } + + # TODO: do we support multiple msps? + echo "[${channel}] Sign the channel update tx ${updateTx} by $mspId $mspPath" + signUpdateTx "${mspId}" "${mspPath}" "${updateTx}" + [ $? -ne 0 ] && { echo "[${channel}] Failed to sign the channel config update tx" && exit 1; } + + echo "[${channel}] Send the channel update tx ${updateTx} by $mspId $mspPath to ${ordererTLSRoot}" + sendUpdateTx "${channel}" "${mspId}" "${mspPath}" "${ordererURL}" "${ordererTLSRoot}" "${updateTx}" + [ $? -ne 0 ] && { echo "[${channel}] Failed to send the channel update tx to ${ordererURL}" && exit 1; } +} + +if [ $# -lt 4 ]; then + echo "Not enough argument supplied" + echo "$(basename $0) channelListFile ordererURL mspId mspPath" + exit 1 +fi +channelListFile=$1 +ordererURL=$2 +mspId=$3 +mspPath=$4 # the msp path + +if [ -f ${channelListFile} ]; then + i=0 + while read channel; do + ((i++)) + echo "Will process channel[$i]=$channel" + if [[ $channel =~ ^#.* ]]; then + echo "channel[$i]=$channel is ignored" + else + UpdateChannel "$channel" "${@:2}" + fi + done <${channelListFile} +else + # for test purpose only + UpdateChannel "$@" # channel, ordererURL, mspid, mspPath +fi diff --git a/hyperledger_fabric/test/fetch-config-block.sh b/hyperledger_fabric/test/fetch-config-block.sh index 459a1d26..c4450cc7 100644 --- a/hyperledger_fabric/test/fetch-config-block.sh +++ b/hyperledger_fabric/test/fetch-config-block.sh @@ -61,12 +61,12 @@ fi main() { if [ $# -lt 3 ]; then echo "Not enough argument supplied" - echo "$(basename $0) mspId channel ordererURL mspPath=${PWD}/msp-mspId" + echo "$(basename $0) channel ordererURL mspId mspPath=${PWD}/msp-mspId" exit 1 fi - local mspId=$1 - local channel=$2 - local ordererURL=$3 + local channel=$1 + local ordererURL=$2 + local mspId=$3 local mspPath=${4:-${PWD}/msp-${mspId}} # Suppose the local msp path named as msp-${msp_id} export FABRIC_LOGGING_SPEC="debug" diff --git a/hyperledger_fabric/v2.1.0/scripts/json_flatter.py b/hyperledger_fabric/v2.1.0/scripts/json_flatter.py index c931d7a6..4916abf4 100644 --- a/hyperledger_fabric/v2.1.0/scripts/json_flatter.py +++ b/hyperledger_fabric/v2.1.0/scripts/json_flatter.py @@ -81,7 +81,7 @@ def process(directory): f_read.close() f_write.close() else: - print("Ignore non-json file {}".format(f)) + print("Ignore non .block.json file {}".format(f)) # Usage python json_flatter.py [path_containing_json_files] diff --git a/hyperledger_fabric/v2.2.4/scripts/func.sh b/hyperledger_fabric/v2.2.4/scripts/func.sh index 1781c83c..59513a51 100644 --- a/hyperledger_fabric/v2.2.4/scripts/func.sh +++ b/hyperledger_fabric/v2.2.4/scripts/func.sh @@ -1044,7 +1044,7 @@ configtxlatorEncode() { } # configtxlator decode pb to json -# Usage: configtxlatorEncode msgType input output +# Usage: configtxlatorDecode msgType input output configtxlatorDecode() { local msgType=$1 local input=$2 diff --git a/hyperledger_fabric/v2.3.3/scripts/json_flatter.py b/hyperledger_fabric/v2.3.3/scripts/json_flatter.py index 890c5181..4916abf4 100644 --- a/hyperledger_fabric/v2.3.3/scripts/json_flatter.py +++ b/hyperledger_fabric/v2.3.3/scripts/json_flatter.py @@ -20,11 +20,11 @@ def decode_if_b64(raw): except binascii.Error: success = False - # if success: # result_bytes = b'xxxx\xx' - # print('===================Start==================================') - # print(raw) - # print(result) - # print('=====================End===================================') + #if success: # result_bytes = b'xxxx\xx' + #print('===================Start==================================') + #print(raw) + #print(result) + #print('=====================End===================================') return success, result @@ -44,7 +44,7 @@ def check_tree(tree, prefix, f_write): else: # leaf result = v if 'cert' in k or 'id_bytes' in k or 'value' in k and 'hash' not in k: - # print(prefix_path) + #print(prefix_path) success, result = decode_if_b64(v) if success: result = "b64({})".format(result) @@ -76,13 +76,12 @@ def process(directory): if f.endswith(".block.json"): file_name = os.path.join(json_dir, f) f_read = open(file=file_name, mode="r", encoding='utf-8') - f_write = open(file=file_name + "-flat.json", mode="w", - encoding='utf-8') + f_write = open(file=file_name+"-flat.json", mode="w", encoding='utf-8') check_tree(json.load(f_read), "", f_write) f_read.close() f_write.close() else: - print("Ignore non-json file {}".format(f)) + print("Ignore non .block.json file {}".format(f)) # Usage python json_flatter.py [path_containing_json_files]