diff --git a/.github/workflows/publish-docker-on-main.yml b/.github/workflows/publish-docker-on-main.yml index e7982fd80..c76586b28 100644 --- a/.github/workflows/publish-docker-on-main.yml +++ b/.github/workflows/publish-docker-on-main.yml @@ -39,4 +39,4 @@ jobs: --platform linux/amd64,linux/arm64 \ --tag "ghcr.io/fablo-io/fablo-kv-node-chaincode-sample:$FABLO_VERSION" \ --push \ - samples/chaincodes/chaincode-kv-node + samples/chaincodes/chaincode-kv-node \ No newline at end of file diff --git a/.gitignore b/.gitignore index 52740672f..b4c27d5d8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ node_modules .idea .vscode samples/invalid-fablo-config.json -.DS_Store \ No newline at end of file +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c260bd17..21f9a4218 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ [#569](https://github.com/hyperledger-labs/fablo/pull/569) * Export network topology with Mermaid [#565](https://github.com/hyperledger-labs/fablo/pull/565) +* Support installing Chaincode from Docker image using CCaaS + [#550](https://github.com/hyperledger-labs/fablo/pull/550) + [#582](https://github.com/hyperledger-labs/fablo/pull/582) ## 2.2.0 diff --git a/COVERAGE_TRACKER.md b/COVERAGE_TRACKER.md index b35fcbff6..80ad1d8fe 100644 --- a/COVERAGE_TRACKER.md +++ b/COVERAGE_TRACKER.md @@ -22,7 +22,7 @@ This document provides an overview of Fablo features. The table below tracks fea | Node | ✓ | ✓ | | | | | Go | ✓ | ✓ | | | | | Java | ✓ | ✓ | | | | -| Chaincode-as-a-Service (CCaaS) | ✕ | ✕ | | | | +| Chaincode-as-a-Service (CCaaS) | ✓ | ✕ | | [04_v2](/e2e-network/docker/test-04-v2-snapshot.sh) | | | Endorsement Policies | ✓ | ✓ | | | | | Multi-org Endorsements | ✓ | ✓ | | | | | Private Data Collections | ✓ | ✓ | | | | diff --git a/README.md b/README.md index 2091bb3c9..fded7c99d 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ Example: 3. Execute `./fablo prune` to destroy the current network. If the network was present, Fablo would not be able to restore the new one from backup. 4. Execute `./fablo restore /tmp/my-snapshot` to restore the network. 5. Execute `./fablo start` to start the restored network. +6. When running external chaincodes(CCAAS), Execute `./fablo chaincodes install` to start the CCAAS container Typically, a snapshot of the network with little data will take less than 1 MB, so it is easy to share. diff --git a/docs/schema.json b/docs/schema.json index a5548e3ee..23b7bd8cb 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -416,8 +416,20 @@ "name", "version", "lang", - "channel", - "directory" + "channel" + ], + "allOf": [ + { + "if": { + "properties": { "lang": { "const": "ccaas"}} + }, + "then": { + "required": [ "image"] + }, + "else": { + "required": [ "directory" ] + } + } ], "properties": { "name": { @@ -439,7 +451,8 @@ "enum": [ "golang", "java", - "node" + "node", + "ccaas" ] }, "channel": { @@ -478,6 +491,11 @@ "title": "Chaincode directory", "type": "string" }, + "image": { + "$id": "#/properties/chaincodes/items/properties/image", + "title": "Chaincode image URI", + "type": "string" + }, "privateData": { "$id": "#/properties/chaincodes/items/properties/privateData", "title": "Private data collections", diff --git a/e2e-network/docker/test-01-v2-simple.sh b/e2e-network/docker/test-01-v2-simple.sh index 03ed03b6e..e6ce2110d 100755 --- a/e2e-network/docker/test-01-v2-simple.sh +++ b/e2e-network/docker/test-01-v2-simple.sh @@ -91,7 +91,6 @@ expectInvoke "peer0.org1.example.com" "my-channel1" "chaincode1" \ '{"Args":["KVContract:put", "name", "James Bond"]}' \ '{\"success\":\"OK\"}' - # Test export-network-topology to Mermaid cp -f "$FABLO_HOME/samples/fablo-config-hlf2-1org-1chaincode.json" "$TEST_TMP/simple-config.json" (cd "$TEST_TMP" && "$FABLO_HOME/fablo.sh" export-network-topology simple-config.json simple-network-topology.mmd) diff --git a/e2e-network/docker/test-04-v2-snapshot.sh b/e2e-network/docker/test-04-v2-snapshot.sh index 9d7795d0b..2d370f245 100755 --- a/e2e-network/docker/test-04-v2-snapshot.sh +++ b/e2e-network/docker/test-04-v2-snapshot.sh @@ -111,6 +111,7 @@ hook_command="perl -i -pe 's/FABRIC_VERSION=2\.3\.3/FABRIC_VERSION=2\.4\.2/g' ./ "$FABLO_HOME/fablo.sh" prune && "$FABLO_HOME/fablo.sh" restore "$snapshot_name" "$hook_command" && "$FABLO_HOME/fablo.sh" start + "$FABLO_HOME/fablo.sh" chaincodes install ) waitForContainer "explorer.example.com" "Successfully created channel event hub for \[my-channel1\]" "200" @@ -129,3 +130,5 @@ expectInvokeRest "$fablo_rest_org1 $user_token" "my-channel1" "chaincode1" \ expectInvokeRest "$fablo_rest_org1 $user_token" "my-channel1" "chaincode1" \ "KVContract:getPrivateMessage" '["_implicit_org_Org1MSP"]' \ '{"success":"RHIgVmljdG9yIEZyaWVz"}' + +echo "✅ Test passed!" diff --git a/e2e/__snapshots__/extendConfig.test.ts.snap b/e2e/__snapshots__/extendConfig.test.ts.snap index 8451f044c..e651bf26d 100644 --- a/e2e/__snapshots__/extendConfig.test.ts.snap +++ b/e2e/__snapshots__/extendConfig.test.ts.snap @@ -244,6 +244,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode.json 1`] = ` }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": undefined, + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -334,6 +335,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode.json 1`] = ` }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -1070,6 +1072,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-k8s.json 1`] = }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": undefined, + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -1160,6 +1163,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-k8s.json 1`] = }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -1886,6 +1890,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.j }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": undefined, + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -1971,6 +1976,7 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.j }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -2681,8 +2687,9 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.j ], "profileName": "MyChannel1", }, - "directory": "./chaincodes/chaincode-kv-node", + "directory": undefined, "endorsement": undefined, + "image": "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0", "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -2766,8 +2773,16 @@ exports[`extend config samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.j }, }, }, - "lang": "node", + "lang": "ccaas", "name": "chaincode1", + "peerChaincodeInstances": [ + { + "containerName": "ccaas-peer0.org1.example.com-chaincode1", + "orgDomain": "org1.example.com", + "peerAddress": "peer0.org1.example.com", + "port": 17041, + }, + ], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -3557,6 +3572,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-private-data. }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": "OR('Org1MSP.member', 'Org2MSP.member')", + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -3647,6 +3663,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-private-data. }, "lang": "node", "name": "or-policy-chaincode", + "peerChaincodeInstances": [], "privateData": [ { "blockToLive": 0, @@ -3965,6 +3982,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-private-data. }, "directory": "./chaincodes/chaincode-kv-go", "endorsement": "AND('Org1MSP.member', 'Org2MSP.member')", + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -4055,6 +4073,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-private-data. }, "lang": "golang", "name": "and-policy-chaincode", + "peerChaincodeInstances": [], "privateData": [ { "blockToLive": 0, @@ -5034,6 +5053,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml 1`] }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": "OR ('Org1MSP.member', 'Org2MSP.member')", + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -5129,6 +5149,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml 1`] }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -5481,6 +5502,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml 1`] }, "directory": "./chaincodes/chaincode-java-simple", "endorsement": "OR ('Org1MSP.member', 'Org2MSP.member')", + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -5576,6 +5598,7 @@ exports[`extend config samples/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml 1`] }, "lang": "java", "name": "chaincode2", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -7601,6 +7624,7 @@ exports[`extend config samples/fablo-config-hlf2-3orgs-1chaincode-raft-explorer. }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": undefined, + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -7739,6 +7763,7 @@ exports[`extend config samples/fablo-config-hlf2-3orgs-1chaincode-raft-explorer. }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -10226,6 +10251,7 @@ exports[`extend config samples/fablo-config-hlf3-1orgs-1chaincode.json 1`] = ` }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": "AND ('Org1MSP.member')", + "image": undefined, "init": "{"Args":[]}", "instantiatingOrg": { "anchorPeers": [ @@ -10316,6 +10342,7 @@ exports[`extend config samples/fablo-config-hlf3-1orgs-1chaincode.json 1`] = ` }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -11184,6 +11211,7 @@ exports[`extend config samples/fablo-config-hlf3-bft-1orgs-1chaincode.json 1`] = }, "directory": "./chaincodes/chaincode-kv-node", "endorsement": "AND ('Org1MSP.member')", + "image": undefined, "init": "{"Args":[]}", "instantiatingOrg": { "anchorPeers": [ @@ -11274,6 +11302,7 @@ exports[`extend config samples/fablo-config-hlf3-bft-1orgs-1chaincode.json 1`] = }, "lang": "node", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -12097,8 +12126,9 @@ exports[`extend config samples/invalid-fablo-config.json 1`] = ` ], "profileName": "MyChannel1", }, - "directory": "./chaincodes/chaincode-kv-node", + "directory": undefined, "endorsement": undefined, + "image": "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0", "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -12182,8 +12212,16 @@ exports[`extend config samples/invalid-fablo-config.json 1`] = ` }, }, }, - "lang": "node", + "lang": "ccaas", "name": "chaincode1", + "peerChaincodeInstances": [ + { + "containerName": "ccaas-peer0.org1.example.com-chaincode1", + "orgDomain": "org1.example.com", + "peerAddress": "peer0.org1.example.com", + "port": 17041, + }, + ], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", @@ -12862,6 +12900,7 @@ exports[`extend config samples/java-dev-mode-sample.json 1`] = ` }, "directory": "./chaincodes/java-chaincode", "endorsement": undefined, + "image": undefined, "initRequired": false, "instantiatingOrg": { "anchorPeers": [ @@ -12928,6 +12967,7 @@ exports[`extend config samples/java-dev-mode-sample.json 1`] = ` }, "lang": "java", "name": "chaincode1", + "peerChaincodeInstances": [], "privateData": [], "privateDataConfigFile": undefined, "version": "0.0.1", diff --git a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.json.test.ts.snap index eb6798424..1bad7b634 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-peer-dev-mode.json.test.ts.snap @@ -1604,7 +1604,7 @@ installChannels() { installChaincodes() { if [ -n "$(ls "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node")" ]; then printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" else @@ -1633,7 +1633,7 @@ installChaincode() { chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1654,7 +1654,7 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode1" ]; then local version="0.0.1" printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1680,7 +1680,7 @@ upgradeChaincode() { chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1716,19 +1716,19 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -1936,6 +1936,8 @@ services: - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=peer0Password # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start --peer-chaincodedev=true ports: @@ -2012,6 +2014,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -2282,7 +2348,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -2303,7 +2368,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -2348,7 +2412,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -2376,6 +2439,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2399,6 +2526,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2454,6 +2657,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-raft-explorer.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-raft-explorer.json.test.ts.snap index ca8021a99..efe5a6118 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-raft-explorer.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode-raft-explorer.json.test.ts.snap @@ -1714,18 +1714,19 @@ installChannels() { } installChaincodes() { - if [ -n "$(ls "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node")" ]; then + if [ -n "$(ls "$CHAINCODES_BASE_DIR/")" ]; then local version="0.0.1" printHeadline "Packaging chaincode 'chaincode1'" "U1F60E" - chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" - chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" + chaincodePackageCCaaS "cli.org1.example.com" "peer0.org1.example.com" "chaincode1" "$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "ccaas-peer0.org1.example.com-chaincode1" "true" + printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + startCCaaSContainer "peer0.org1.example.com:7041" "chaincode1" "chaincode1_$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "cli.org1.example.com" "crypto-orderer/tlsca.orderer.example.com-cert.pem" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "ccaas" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' installation. Chaincode directory is empty." - echo "Looked in dir: '$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node'" + echo "Looked in dir: '$CHAINCODES_BASE_DIR/'" fi } @@ -1744,18 +1745,19 @@ installChaincode() { fi if [ "$chaincodeName" = "chaincode1" ]; then - if [ -n "$(ls "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node")" ]; then + if [ -n "$(ls "$CHAINCODES_BASE_DIR/")" ]; then printHeadline "Packaging chaincode 'chaincode1'" "U1F60E" - chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" - chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" + chaincodePackageCCaaS "cli.org1.example.com" "peer0.org1.example.com" "chaincode1" "$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "ccaas-peer0.org1.example.com-chaincode1" "true" + printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + startCCaaSContainer "peer0.org1.example.com:7041" "chaincode1" "chaincode1_$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "cli.org1.example.com" "crypto-orderer/tlsca.orderer.example.com-cert.pem" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "ccaas" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' install. Chaincode directory is empty." - echo "Looked in dir: '$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node'" + echo "Looked in dir: '$CHAINCODES_BASE_DIR/'" fi fi } @@ -1770,7 +1772,7 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode1" ]; then local version="0.0.1" printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1791,18 +1793,19 @@ upgradeChaincode() { fi if [ "$chaincodeName" = "chaincode1" ]; then - if [ -n "$(ls "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node")" ]; then + if [ -n "$(ls "$CHAINCODES_BASE_DIR/")" ]; then printHeadline "Packaging chaincode 'chaincode1'" "U1F60E" - chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" - chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" + chaincodePackageCCaaS "cli.org1.example.com" "peer0.org1.example.com" "chaincode1" "$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "ccaas-peer0.org1.example.com-chaincode1" "true" + printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + startCCaaSContainer "peer0.org1.example.com:7041" "chaincode1" "chaincode1_$version" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" "17041" "cli.org1.example.com" "crypto-orderer/tlsca.orderer.example.com-cert.pem" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "ccaas" "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' upgrade. Chaincode directory is empty." - echo "Looked in dir: '$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node'" + echo "Looked in dir: '$CHAINCODES_BASE_DIR/'" fi fi } @@ -1831,19 +1834,19 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -1909,7 +1912,7 @@ services: - ../fabric-config/crypto-config/peerOrganizations/orderer.example.com/peers/orderer0.group1.orderer.example.com/msp/tlscacerts/tlsca.orderer.example.com-cert.pem:/var/hyperledger/cli/crypto-orderer/tlsca.orderer.example.com-cert.pem:ro - ../fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt:/var/hyperledger/cli/crypto-peer/peer0.org1.example.com/tls/ca.crt:ro - ../fabric-config/chaincode-packages:/var/hyperledger/cli/chaincode-packages/ - - "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node/:/var/hyperledger/cli/chaincode1/" + - "$CHAINCODES_BASE_DIR//:/var/hyperledger/cli/chaincode1/" networks: - basic @@ -2022,7 +2025,7 @@ services: - ../fabric-config/crypto-config/peerOrganizations/orderer.example.com/peers/orderer0.group1.orderer.example.com/msp/tlscacerts/tlsca.orderer.example.com-cert.pem:/var/hyperledger/cli/crypto-orderer/tlsca.orderer.example.com-cert.pem:ro - ../fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt:/var/hyperledger/cli/crypto-peer/peer0.org1.example.com/tls/ca.crt:ro - ../fabric-config/chaincode-packages:/var/hyperledger/cli/chaincode-packages/ - - "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node/:/var/hyperledger/cli/chaincode1/" + - "$CHAINCODES_BASE_DIR//:/var/hyperledger/cli/chaincode1/" networks: - basic @@ -2088,6 +2091,8 @@ services: - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=peer0Password # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2206,6 +2211,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -2476,7 +2545,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -2497,7 +2565,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -2542,7 +2609,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -2570,6 +2636,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2593,6 +2723,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2648,6 +2854,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode.json.test.ts.snap index a39820a14..82ea291d8 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-1org-1chaincode.json.test.ts.snap @@ -1675,7 +1675,7 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" else @@ -1705,7 +1705,7 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1726,7 +1726,7 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode1" ]; then local version="0.0.1" printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1753,7 +1753,7 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -1788,27 +1788,27 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer1.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -1972,6 +1972,8 @@ services: - GODEBUG=netdns=go # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2016,6 +2018,8 @@ services: - GODEBUG=netdns=go # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2073,6 +2077,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -2343,7 +2411,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -2364,7 +2431,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -2409,7 +2475,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -2437,6 +2502,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2460,6 +2589,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2515,6 +2720,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-private-data.yaml.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-private-data.yaml.test.ts.snap index aeb7fcbae..eeac3d8d1 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-private-data.yaml.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-private-data.yaml.test.ts.snap @@ -2391,10 +2391,10 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "node" printHeadline "Installing 'or-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printHeadline "Installing 'or-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printItalics "Committing chaincode 'or-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/or-policy-chaincode.json" else @@ -2408,10 +2408,10 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "golang" printHeadline "Installing 'and-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printHeadline "Installing 'and-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printItalics "Committing chaincode 'and-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/and-policy-chaincode.json" else @@ -2441,10 +2441,10 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "node" printHeadline "Installing 'or-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printHeadline "Installing 'or-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printItalics "Committing chaincode 'or-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/or-policy-chaincode.json" @@ -2460,10 +2460,10 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "golang" printHeadline "Installing 'and-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printHeadline "Installing 'and-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printItalics "Committing chaincode 'and-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/and-policy-chaincode.json" @@ -2484,9 +2484,9 @@ runDevModeChaincode() { if [ "$chaincodeName" = "or-policy-chaincode" ]; then local version="0.0.1" printHeadline "Approving 'or-policy-chaincode' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "" "" printHeadline "Approving 'or-policy-chaincode' for Org2 (dev mode)" "U1F60E" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "" "" printItalics "Committing chaincode 'or-policy-chaincode' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/or-policy-chaincode.json" @@ -2494,9 +2494,9 @@ runDevModeChaincode() { if [ "$chaincodeName" = "and-policy-chaincode" ]; then local version="0.0.1" printHeadline "Approving 'and-policy-chaincode' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "" "" printHeadline "Approving 'and-policy-chaincode' for Org2 (dev mode)" "U1F60E" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "" "" printItalics "Committing chaincode 'and-policy-chaincode' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "0.0.1" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/and-policy-chaincode.json" @@ -2523,10 +2523,10 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "node" printHeadline "Installing 'or-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "or-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printHeadline "Installing 'or-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "or-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/or-policy-chaincode.json" "node" "" printItalics "Committing chaincode 'or-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "or-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "OR('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/or-policy-chaincode.json" @@ -2542,10 +2542,10 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "golang" printHeadline "Installing 'and-policy-chaincode' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "and-policy-chaincode" "$version" "" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printHeadline "Installing 'and-policy-chaincode' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7061" "and-policy-chaincode" "$version" "" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7061" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "collections/and-policy-chaincode.json" "golang" "" printItalics "Committing chaincode 'and-policy-chaincode' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "and-policy-chaincode" "$version" "orderer0.group1.orderer.example.com:7030" "AND('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7041,peer0.org2.example.com:7061" "" "collections/and-policy-chaincode.json" @@ -2583,59 +2583,59 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-or-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-or-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-or-policy-chaincode*" -q); do + for image in $(docker images "peer0.org1.example.com-or-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-or-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-or-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-or-policy-chaincode*" -q); do + for image in $(docker images "peer1.org1.example.com-or-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer0.org2.example.com-or-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org2.example.com-or-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org2.example.com-or-policy-chaincode*" -q); do + for image in $(docker images "peer0.org2.example.com-or-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-and-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-and-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-and-policy-chaincode*" -q); do + for image in $(docker images "peer0.org1.example.com-and-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-and-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-and-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-and-policy-chaincode*" -q); do + for image in $(docker images "peer1.org1.example.com-and-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer0.org2.example.com-and-policy-chaincode" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org2.example.com-and-policy-chaincode" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org2.example.com-and-policy-chaincode*" -q); do + for image in $(docker images "peer0.org2.example.com-and-policy-chaincode*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -2805,6 +2805,8 @@ services: - GODEBUG=netdns=go # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2849,6 +2851,8 @@ services: - GODEBUG=netdns=go # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2942,6 +2946,8 @@ services: - GODEBUG=netdns=go # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org2"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2999,6 +3005,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -3269,7 +3339,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -3290,7 +3359,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -3335,7 +3403,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -3363,6 +3430,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -3386,6 +3517,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -3441,6 +3648,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml.test.ts.snap index 02bdde064..45efd9f2b 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-2orgs-2chaincodes-raft.yaml.test.ts.snap @@ -3508,10 +3508,10 @@ installChaincodes() { chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printHeadline "Installing 'chaincode1' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7081" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer0.org1.example.com:7061,peer0.org2.example.com:7081" "crypto-peer/peer0.org1.example.com/tls/ca.crt,crypto-peer/peer0.org2.example.com/tls/ca.crt" "" else @@ -3524,10 +3524,10 @@ installChaincodes() { chaincodeBuild "chaincode2" "java" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-java-simple" "16" chaincodePackage "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "java" printHeadline "Installing 'chaincode2' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printHeadline "Installing 'chaincode2' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer1.org2.example.com:7082" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printItalics "Committing chaincode 'chaincode2' on channel 'my-channel2' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer1.org1.example.com:7062,peer1.org2.example.com:7082" "crypto-peer/peer1.org1.example.com/tls/ca.crt,crypto-peer/peer1.org2.example.com/tls/ca.crt" "" else @@ -3556,10 +3556,10 @@ installChaincode() { chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printHeadline "Installing 'chaincode1' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7081" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer0.org1.example.com:7061,peer0.org2.example.com:7081" "crypto-peer/peer0.org1.example.com/tls/ca.crt,crypto-peer/peer0.org2.example.com/tls/ca.crt" "" @@ -3574,10 +3574,10 @@ installChaincode() { chaincodeBuild "chaincode2" "java" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-java-simple" "16" chaincodePackage "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "java" printHeadline "Installing 'chaincode2' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printHeadline "Installing 'chaincode2' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer1.org2.example.com:7082" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printItalics "Committing chaincode 'chaincode2' on channel 'my-channel2' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer1.org1.example.com:7062,peer1.org2.example.com:7082" "crypto-peer/peer1.org1.example.com/tls/ca.crt,crypto-peer/peer1.org2.example.com/tls/ca.crt" "" @@ -3598,9 +3598,9 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode1" ]; then local version="0.0.1" printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" "" "" printHeadline "Approving 'chaincode1' for Org2 (dev mode)" "U1F60E" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer0.org1.example.com:7061,peer0.org2.example.com:7081" "" "" @@ -3608,9 +3608,9 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode2" ]; then local version="0.0.1" printHeadline "Approving 'chaincode2' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" "" "" printHeadline "Approving 'chaincode2' for Org2 (dev mode)" "U1F60E" - chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" + chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode2' on channel 'my-channel2' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "0.0.1" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "" "peer1.org1.example.com:7062,peer1.org2.example.com:7082" "" "" @@ -3636,10 +3636,10 @@ upgradeChaincode() { chaincodeBuild "chaincode1" "node" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node" "16" chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7061" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printHeadline "Installing 'chaincode1' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer0.org2.example.com:7081" "chaincode1" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer0.org2.example.com:7081" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7061" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer0.org1.example.com:7061,peer0.org2.example.com:7081" "crypto-peer/peer0.org1.example.com/tls/ca.crt,crypto-peer/peer0.org2.example.com/tls/ca.crt" "" @@ -3654,10 +3654,10 @@ upgradeChaincode() { chaincodeBuild "chaincode2" "java" "$CHAINCODES_BASE_DIR/./chaincodes/chaincode-java-simple" "16" chaincodePackage "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "java" printHeadline "Installing 'chaincode2' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7062" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printHeadline "Installing 'chaincode2' for Org2" "U1F60E" chaincodeInstall "cli.org2.example.com" "peer1.org2.example.com:7082" "chaincode2" "$version" "crypto-orderer/tlsca.orderer1.com-cert.pem" - chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" + chaincodeApprove "cli.org2.example.com" "peer1.org2.example.com:7082" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "" "java" "" printItalics "Committing chaincode 'chaincode2' on channel 'my-channel2' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer1.org1.example.com:7062" "my-channel2" "chaincode2" "$version" "orderer0.group1.orderer1.com:7030" "OR ('Org1MSP.member', 'Org2MSP.member')" "false" "crypto-orderer/tlsca.orderer1.com-cert.pem" "peer1.org1.example.com:7062,peer1.org2.example.com:7082" "crypto-peer/peer1.org1.example.com/tls/ca.crt,crypto-peer/peer1.org2.example.com/tls/ca.crt" "" @@ -3707,43 +3707,43 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer0.org2.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org2.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org2.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org2.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-chaincode2" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-chaincode2" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-chaincode2*" -q); do + for image in $(docker images "peer1.org1.example.com-chaincode2*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org2.example.com-chaincode2" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org2.example.com-chaincode2" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org2.example.com-chaincode2*" -q); do + for image in $(docker images "peer1.org2.example.com-chaincode2*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -4136,6 +4136,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4185,6 +4187,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4294,6 +4298,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org2"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4343,6 +4349,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org2"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4400,6 +4408,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -4670,7 +4742,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -4691,7 +4762,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -4736,7 +4806,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -4764,6 +4833,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -4787,6 +4920,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -4842,6 +5051,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf2-3orgs-1chaincode-raft-explorer.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf2-3orgs-1chaincode-raft-explorer.json.test.ts.snap index a3da65926..189f84ed3 100644 --- a/e2e/__snapshots__/fablo-config-hlf2-3orgs-1chaincode-raft-explorer.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf2-3orgs-1chaincode-raft-explorer.json.test.ts.snap @@ -3843,7 +3843,7 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else @@ -3873,7 +3873,7 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" @@ -3894,7 +3894,7 @@ runDevModeChaincode() { if [ "$chaincodeName" = "chaincode1" ]; then local version="0.0.1" printHeadline "Approving 'chaincode1' for Org1 (dev mode)" "U1F60E" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "" "" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1' (dev mode)" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "0.0.1" "orderer0.group1.orderer.example.com:7030" "" "false" "" "peer0.org1.example.com:7041" "" "" @@ -3921,7 +3921,7 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "" "false" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" @@ -3965,27 +3965,27 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer1.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -4303,6 +4303,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4350,6 +4352,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4532,6 +4536,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org2"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4579,6 +4585,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org2"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4761,6 +4769,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org3"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4808,6 +4818,8 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/peer/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/peer/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org3"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -4908,6 +4920,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -5178,7 +5254,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -5199,7 +5274,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -5244,7 +5318,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -5272,6 +5345,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -5295,6 +5432,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -5350,6 +5563,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf3-1orgs-1chaincode.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf3-1orgs-1chaincode.json.test.ts.snap index 20ebe22a0..25aff19fb 100644 --- a/e2e/__snapshots__/fablo-config-hlf3-1orgs-1chaincode.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf3-1orgs-1chaincode.json.test.ts.snap @@ -1760,9 +1760,9 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' installation. Chaincode directory is empty." echo "Looked in dir: '$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node'" @@ -1790,9 +1790,9 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' install. Chaincode directory is empty." @@ -1826,9 +1826,9 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' upgrade. Chaincode directory is empty." @@ -1854,27 +1854,27 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer1.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -2208,6 +2208,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2257,6 +2259,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2314,6 +2318,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -2590,7 +2658,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -2611,7 +2678,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -2656,7 +2722,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -2684,6 +2749,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2707,6 +2836,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2762,6 +2967,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fablo-config-hlf3-bft-1orgs-1chaincode.json.test.ts.snap b/e2e/__snapshots__/fablo-config-hlf3-bft-1orgs-1chaincode.json.test.ts.snap index c1220b215..dfa538374 100644 --- a/e2e/__snapshots__/fablo-config-hlf3-bft-1orgs-1chaincode.json.test.ts.snap +++ b/e2e/__snapshots__/fablo-config-hlf3-bft-1orgs-1chaincode.json.test.ts.snap @@ -1784,9 +1784,9 @@ installChaincodes() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' installation. Chaincode directory is empty." echo "Looked in dir: '$CHAINCODES_BASE_DIR/./chaincodes/chaincode-kv-node'" @@ -1814,9 +1814,9 @@ installChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' install. Chaincode directory is empty." @@ -1850,9 +1850,9 @@ upgradeChaincode() { chaincodePackage "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "node" printHeadline "Installing 'chaincode1' for Org1" "U1F60E" chaincodeInstall "cli.org1.example.com" "peer0.org1.example.com:7041" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" chaincodeInstall "cli.org1.example.com" "peer1.org1.example.com:7042" "chaincode1" "$version" "crypto-orderer/tlsca.orderer.example.com-cert.pem" - chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" + chaincodeApprove "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "" "node" "" printItalics "Committing chaincode 'chaincode1' on channel 'my-channel1' as 'Org1'" "U1F618" - chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "undefined" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" + chaincodeCommit "cli.org1.example.com" "peer0.org1.example.com:7041" "my-channel1" "chaincode1" "$version" "orderer0.group1.orderer.example.com:7030" "AND ('Org1MSP.member')" "" "crypto-orderer/tlsca.orderer.example.com-cert.pem" "peer0.org1.example.com:7041" "crypto-peer/peer0.org1.example.com/tls/ca.crt" "" else echo "Warning! Skipping chaincode 'chaincode1' upgrade. Chaincode directory is empty." @@ -1878,27 +1878,27 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \\U1F5D1 \\n" - for container in $(docker ps -a | grep "dev-peer0.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer0.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer0.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer0.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done - for container in $(docker ps -a | grep "dev-peer1.org1.example.com-chaincode1" | awk '{print $1}'); do + for container in $(docker ps -a | grep "peer1.org1.example.com-chaincode1" | awk '{print $1}'); do echo "Removing container $container..." docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted" done - for image in $(docker images "dev-peer1.org1.example.com-chaincode1*" -q); do + for image in $(docker images "peer1.org1.example.com-chaincode1*" -q); do echo "Removing image $image..." docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted" done + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \\U1F5D1 \\n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" @@ -2232,6 +2232,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer0Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2281,6 +2283,8 @@ services: - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer/tls/ca.crt # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"peer1Org1"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start ports: @@ -2338,6 +2342,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="\${CONFIG_PATH}ccaas/\${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/tlsca.\${ORG_DOMAIN}-cert.pem" + local CA_KEY="\${CONFIG_PATH}peerOrganizations/\${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for \${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \\ + -v "$OUTPUT_PATH:/certs" \\ + -v "$CA_CERT:/ca/ca.crt:ro" \\ + -v "$CA_KEY:/ca/ca.key:ro" \\ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \\ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for \${CONTAINER_NAME} at \${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate @@ -2614,7 +2682,6 @@ dockerPullIfMissing() { } node_version_check() { - local fabric_shim_version="$1" local nodejs_version @@ -2635,7 +2702,6 @@ node_version_check() { fi echo $nodejs_version - } chaincodeBuild() { @@ -2680,7 +2746,6 @@ chaincodeBuild() { # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -2708,6 +2773,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="\${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\\"type\\":\\"ccaas\\",\\"label\\":\\"$CHAINCODE_LABEL\\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"domain\\": \\"\${ACTUAL_CONTAINER_NAME}\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED, + \\"client_auth_required\\": true, + \\"client_cert\\": \\"$SERVER_CERT\\", + \\"client_key\\": \\"$SERVER_KEY\\", + \\"root_cert\\": \\"$ROOT_CERT\\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \\"address\\": \\"\${ACTUAL_CONTAINER_NAME}:7052\\", + \\"dial_timeout\\": \\"10s\\", + \\"tls_required\\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2731,6 +2860,82 @@ chaincodeInstall() { "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-\${PEER_ADDRESS%%:*}-\${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \\ + --output json \\ + "\${CA_CERT_PARAMS[@]+"\${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="\${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: \${CHAINCODE_LABEL}:\${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="\${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="\${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "\${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \\ + --name "$CONTAINER_NAME" \\ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \\ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \\ + -e CORE_CHAINCODE_ID_NAME="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CHAINCODE_ID="\${CHAINCODE_LABEL}:\${PACKAGE_HASH}" \\ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \\ + -e CORE_CHAINCODE_LOGGING_SHIM=info \\ + -e CORE_PEER_TLS_ENABLED=true \\ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \\ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \\ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \\ + -e CORE_PEER_LOCALMSPID=Org1MSP \\ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \\ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \\ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \\ + -p "$PORT_MAP" \\ + --network "$NETWORK" \\ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -2786,6 +2991,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\\"$CHAINCODE_LABEL\\") ][0].package_id // \\"\\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/e2e/__snapshots__/fabloCommands.test.ts.snap b/e2e/__snapshots__/fabloCommands.test.ts.snap index d6f843437..7192088ba 100644 --- a/e2e/__snapshots__/fabloCommands.test.ts.snap +++ b/e2e/__snapshots__/fabloCommands.test.ts.snap @@ -2009,6 +2009,7 @@ Validation warnings count: 0 } } }, + "peerChaincodeInstances": [], "privateData": [] }, { @@ -2455,6 +2456,7 @@ Validation warnings count: 0 } } }, + "peerChaincodeInstances": [], "privateData": [] } ], diff --git a/e2e/__snapshots__/schema.test.ts.snap b/e2e/__snapshots__/schema.test.ts.snap index 6cd28ee15..8bbbc9bed 100644 --- a/e2e/__snapshots__/schema.test.ts.snap +++ b/e2e/__snapshots__/schema.test.ts.snap @@ -24,6 +24,27 @@ exports[`schema should match snapshot 1`] = ` ], "items": { "$id": "#/properties/chaincodes/items", + "allOf": [ + { + "else": { + "required": [ + "directory", + ], + }, + "if": { + "properties": { + "lang": { + "const": "ccaas", + }, + }, + }, + "then": { + "required": [ + "image", + ], + }, + }, + ], "properties": { "channel": { "$id": "#/properties/chaincodes/items/properties/channel", @@ -51,6 +72,11 @@ exports[`schema should match snapshot 1`] = ` "title": "Endorsement configuration", "type": "string", }, + "image": { + "$id": "#/properties/chaincodes/items/properties/image", + "title": "Chaincode image URI", + "type": "string", + }, "init": { "$id": "#/properties/chaincodes/items/properties/init", "title": "Initialization arguments (for Hyperledger Fabric below 2.0)", @@ -67,6 +93,7 @@ exports[`schema should match snapshot 1`] = ` "golang", "java", "node", + "ccaas", ], "title": "Language", "type": "string", @@ -132,7 +159,6 @@ exports[`schema should match snapshot 1`] = ` "version", "lang", "channel", - "directory", ], "title": "Chaincode", "type": "object", diff --git a/e2e/schema.test.ts b/e2e/schema.test.ts index 40a3b87ee..af2df3611 100644 --- a/e2e/schema.test.ts +++ b/e2e/schema.test.ts @@ -251,7 +251,7 @@ describe("schema", () => { it("should validate chaincode language", () => { const withChaincodeLanguage = (l: string) => updatedBase((json: FabloConfigJson) => { - json.chaincodes[0].lang = l as "java" | "golang" | "node"; + json.chaincodes[0].lang = l as "java" | "golang" | "node" | "ccaas"; }); expect(withChaincodeLanguage("java")).toMatchSchema(schema); diff --git a/samples/chaincodes/chaincode-kv-node/package.json b/samples/chaincodes/chaincode-kv-node/package.json index 4751eb2e3..f50ae9b09 100644 --- a/samples/chaincodes/chaincode-kv-node/package.json +++ b/samples/chaincodes/chaincode-kv-node/package.json @@ -8,7 +8,7 @@ }, "scripts": { "start": "fabric-chaincode-node start", - "start:ccaas": "fabric-chaincode-node server --chaincode-address 0.0.0.0:7052 --chaincode-id \"chaincode1:0.0.1\"", + "start:ccaas": "fabric-chaincode-node server --chaincode-address 0.0.0.0:7052 --chaincode-id \"$CHAINCODE_ID\"", "start:dev": "fabric-chaincode-node start --peer.address \"127.0.0.1:8541\" --chaincode-id-name \"chaincode1:0.0.1\" --tls.enabled false", "start:watch": "nodemon --exec \"npm run start:dev\"", "build": "echo \"No need to build the chaincode\"", diff --git a/samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.json b/samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.json index 71969cf3d..c07880f57 100644 --- a/samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.json +++ b/samples/fablo-config-hlf2-1org-1chaincode-raft-explorer.json @@ -55,9 +55,9 @@ { "name": "chaincode1", "version": "0.0.1", - "lang": "node", + "lang": "ccaas", "channel": "my-channel1", - "directory": "./chaincodes/chaincode-kv-node" + "image": "ghcr.io/fablo-io/fablo-sample-kv-node-chaincode:2.2.0" } ], "hooks": { diff --git a/src/extend-config/extendChaincodesConfig.ts b/src/extend-config/extendChaincodesConfig.ts index da7982046..a70b00b53 100644 --- a/src/extend-config/extendChaincodesConfig.ts +++ b/src/extend-config/extendChaincodesConfig.ts @@ -42,7 +42,7 @@ const extendChaincodesConfig = ( transformedChannels: ChannelConfig[], network: Global, ): ChaincodeConfig[] => { - return chaincodes.map((chaincode) => { + return chaincodes.map((chaincode, index) => { const channel = transformedChannels.find((c) => c.name === chaincode.channel); if (!channel) throw new Error(`No matching channel with name '${chaincode.channel}'`); @@ -57,16 +57,36 @@ const extendChaincodesConfig = ( ); const privateDataConfigFile = privateData.length > 0 ? `collections/${chaincode.name}.json` : undefined; + const peerChaincodeInstances = !chaincode.image + ? [] + : channel.orgs.flatMap((org) => + org.peers.map((peer) => { + return { + containerName: `ccaas-${peer.address}-${chaincode.name}`, + peerAddress: peer.address, + port: 10000 * (index + 1) + peer.port, + orgDomain: org.domain, + }; + }), + ); + + if (chaincode.lang === "ccaas") { + if (!chaincode.image) { + throw new Error(`Chaincode '${chaincode.name}' of type 'ccaas' must specify an image field`); + } + } return { directory: chaincode.directory, name: chaincode.name, version: chaincode.version, lang: chaincode.lang, channel, + image: chaincode.image, ...initParams, endorsement, instantiatingOrg: channel.instantiatingOrg, privateDataConfigFile, + peerChaincodeInstances, privateData, }; }); diff --git a/src/init/index.ts b/src/init/index.ts index 970ac29c3..b8f908deb 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -1,10 +1,11 @@ import * as Generator from "yeoman-generator"; import * as chalk from "chalk"; import { GlobalJson, FabloConfigJson } from "../types/FabloConfigJson"; +import { version } from "../../package.json"; function getDefaultFabloConfig(): FabloConfigJson { return { - $schema: "https://github.com/hyperledger-labs/fablo/releases/download/2.2.0/schema.json", + $schema: `https://github.com/hyperledger-labs/fablo/releases/download/${version}/schema.json`, global: { fabricVersion: "2.5.12", tls: false, diff --git a/src/setup-docker/templates/fabric-docker/commands-generated.sh b/src/setup-docker/templates/fabric-docker/commands-generated.sh index b22588f21..71ac171e2 100644 --- a/src/setup-docker/templates/fabric-docker/commands-generated.sh +++ b/src/setup-docker/templates/fabric-docker/commands-generated.sh @@ -246,20 +246,20 @@ stopNetwork() { } networkDown() { - printHeadline "Destroying network" "U1F916" - (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) - printf "Removing chaincode containers & images... \U1F5D1 \n" <% chaincodes.forEach((chaincode) => { -%> <% chaincode.channel.orgs.forEach((org) => { -%> <% org.peers.forEach((peer) => { -%> - <% const chaincodeContainerName=`dev-${peer.address}-${chaincode.name}` -%> + <% const chaincodeContainerName=`${peer.address}-${chaincode.name}` -%> for container in $(docker ps -a | grep "<%= chaincodeContainerName %>" | awk '{print $1}'); do echo "Removing container $container..."; docker rm -f "$container" || echo "docker rm of $container failed. Check if all fabric dockers properly was deleted"; done for image in $(docker images "<%= chaincodeContainerName %>*" -q); do echo "Removing image $image..."; docker rmi "$image" || echo "docker rmi of $image failed. Check if all fabric dockers properly was deleted"; done <% }) -%> <% }) -%> <% }) -%> + printHeadline "Destroying network" "U1F916" + (cd "$FABLO_NETWORK_ROOT"/fabric-docker && docker compose down) + printf "Removing generated configs... \U1F5D1 \n" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/config" rm -rf "$FABLO_NETWORK_ROOT/fabric-config/crypto-config" diff --git a/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-dev-v2.sh b/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-dev-v2.sh index f165969fd..130691b26 100644 --- a/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-dev-v2.sh +++ b/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-dev-v2.sh @@ -16,7 +16,9 @@ "<%- chaincode.endorsement || '' %>" <% -%> "false" <% -%> "" <% -%> - "<%= chaincode.privateDataConfigFile || '' %>" + "<%= chaincode.privateDataConfigFile || '' %>" <% -%> + "" <% -%> + "" <% }) -%> printItalics "Committing chaincode '<%= chaincode.name %>' on channel '<%= chaincode.channel.name %>' as '<%= chaincode.instantiatingOrg.name %>' (dev mode)" "U1F618" chaincodeCommit <% -%> diff --git a/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-install-v2.sh b/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-install-v2.sh index 8455856ee..e68eca65b 100644 --- a/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-install-v2.sh +++ b/src/setup-docker/templates/fabric-docker/commands-generated/chaincode-install-v2.sh @@ -8,26 +8,50 @@ - global */-%> printHeadline "Packaging chaincode '<%= chaincode.name %>'" "U1F60E" -chaincodeBuild <% -%> - "<%= chaincode.name %>" <% -%> - "<%= chaincode.lang %>" <% -%> - "$CHAINCODES_BASE_DIR/<%= chaincode.directory %>" <% -%> - "<%= global.fabricRecommendedNodeVersion %>" -chaincodePackage <% -%> - "<%= chaincode.instantiatingOrg.cli.address %>" <% -%> - "<%= chaincode.instantiatingOrg.headPeer.fullAddress %>" <% -%> - "<%= chaincode.name %>" <% -%> - "$version" <% -%> - "<%= chaincode.lang %>" <% -%> +<% if (chaincode.lang === "ccaas") { -%> + <% chaincode.peerChaincodeInstances.forEach((instance) => { -%> + chaincodePackageCCaaS <% -%> + "<%= chaincode.instantiatingOrg.cli.address %>" <% -%> + "<%= instance.peerAddress %>" <% -%> + "<%= chaincode.name %>" <% -%> + "$version" <% -%> + "<%= chaincode.image %>" <% -%> + "<%= instance.port %>" <% -%> + "<%= instance.containerName %>" <% -%> + "<%= global.tls %>" + <% }) -%> +<% } else { -%> + chaincodeBuild <% -%> + "<%= chaincode.name %>" <% -%> + "<%= chaincode.lang %>" <% -%> + "$CHAINCODES_BASE_DIR/<%= chaincode.directory %>" <% -%> + "<%= global.fabricRecommendedNodeVersion %>" + chaincodePackage <% -%> + "<%= chaincode.instantiatingOrg.cli.address %>" <% -%> + "<%= chaincode.instantiatingOrg.headPeer.fullAddress %>" <% -%> + "<%= chaincode.name %>" <% -%> + "$version" <% -%> + "<%= chaincode.lang %>" <% -%> +<% } -%> <% chaincode.channel.orgs.forEach((org) => { -%> printHeadline "Installing '<%= chaincode.name %>' for <%= org.name %>" "U1F60E" - <% org.peers.forEach((peer) => { -%> + <% org.peers.forEach((peer, i) => { -%> chaincodeInstall <% -%> "<%= org.cli.address %>" <% -%> "<%= peer.fullAddress %>" <% -%> "<%= chaincode.name %>" <% -%> "$version" <% -%> "<%= !global.tls ? '' : `crypto-orderer/tlsca.${chaincode.channel.ordererHead.domain}-cert.pem` %>" + <% if (chaincode.lang === "ccaas") { -%> + startCCaaSContainer <% -%> + "<%= peer.fullAddress %>" <% -%> + "<%= chaincode.name %>" <% -%> + "<%= chaincode.name %>_$version" <% -%> + "<%= chaincode.image %>" <% -%> + "<%= chaincode.peerChaincodeInstances[i].port %>" <% -%> + "<%= org.cli.address %>" <% -%> + "<%= !global.tls ? '' : `crypto-orderer/tlsca.${chaincode.channel.ordererHead.domain}-cert.pem` %>" + <% } -%> <% }) -%> chaincodeApprove <% -%> "<%= org.cli.address %>" <% -%> @@ -37,9 +61,11 @@ chaincodePackage <% -%> "$version" <% -%> "<%= chaincode.channel.ordererHead.fullAddress %>" <% -%> "<%- chaincode.endorsement || '' %>" <% -%> - "<%= `${chaincode.initRequired}` %>" <% -%> + "<%= chaincode.initRequired %>" <% -%> "<%= !global.tls ? '' : `crypto-orderer/tlsca.${chaincode.channel.ordererHead.domain}-cert.pem` %>" <% -%> - "<%= chaincode.privateDataConfigFile || '' %>" + "<%= chaincode.privateDataConfigFile || '' %>" <% -%> + "<%= chaincode.lang %>" <% -%> + "<%= chaincode.lang === 'ccaas' ? chaincode.image : '' %>" <% }) -%> printItalics "Committing chaincode '<%= chaincode.name %>' on channel '<%= chaincode.channel.name %>' as '<%= chaincode.instantiatingOrg.name %>'" "U1F618" chaincodeCommit <% -%> @@ -50,7 +76,7 @@ chaincodeCommit <% -%> "$version" <% -%> "<%= chaincode.channel.ordererHead.fullAddress %>" <% -%> "<%- chaincode.endorsement || '' %>" <% -%> - "<%= `${chaincode.initRequired}` %>" <% -%> + "<%= chaincode.initRequired %>" <% -%> "<%= !global.tls ? '' : `crypto-orderer/tlsca.${chaincode.channel.ordererHead.domain}-cert.pem` %>" <% -%> "<%= chaincode.channel.orgs.map((o) => o.headPeer.fullAddress).join(',') %>" <% -%> "<%= !global.tls ? '' : chaincode.channel.orgs.map(o => `crypto-peer/${o.headPeer.address}/tls/ca.crt`).join(',') %>" <% -%> diff --git a/src/setup-docker/templates/fabric-docker/docker-compose.yaml b/src/setup-docker/templates/fabric-docker/docker-compose.yaml index ed6bc3ecf..a209bd53d 100755 --- a/src/setup-docker/templates/fabric-docker/docker-compose.yaml +++ b/src/setup-docker/templates/fabric-docker/docker-compose.yaml @@ -286,6 +286,8 @@ services: # enabled gateway - CORE_PEER_GATEWAY_ENABLED=true <%_ } _%> + # ccaas builder configuration + - CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"peername":"<%= peer.name %><%= org.name %>"} working_dir: /etc/hyperledger/fabric/peer/ command: peer node start <%= global.peerDevMode ? '--peer-chaincodedev=true' : '' %> ports: diff --git a/src/setup-docker/templates/fabric-docker/scripts/base-functions-v2.sh b/src/setup-docker/templates/fabric-docker/scripts/base-functions-v2.sh index fc2f02f67..fa5215a18 100644 --- a/src/setup-docker/templates/fabric-docker/scripts/base-functions-v2.sh +++ b/src/setup-docker/templates/fabric-docker/scripts/base-functions-v2.sh @@ -38,6 +38,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="${CONFIG_PATH}ccaas/${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="${CONFIG_PATH}peerOrganizations/${ORG_DOMAIN}/tlsca/tlsca.${ORG_DOMAIN}-cert.pem" + local CA_KEY="${CONFIG_PATH}peerOrganizations/${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for ${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \ + -v "$OUTPUT_PATH:/certs" \ + -v "$CA_CERT:/ca/ca.crt:ro" \ + -v "$CA_KEY:/ca/ca.key:ro" \ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for ${CONTAINER_NAME} at ${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate diff --git a/src/setup-docker/templates/fabric-docker/scripts/base-functions-v3.sh b/src/setup-docker/templates/fabric-docker/scripts/base-functions-v3.sh index 6f7a094cb..41eecf447 100644 --- a/src/setup-docker/templates/fabric-docker/scripts/base-functions-v3.sh +++ b/src/setup-docker/templates/fabric-docker/scripts/base-functions-v3.sh @@ -38,6 +38,70 @@ certsGenerate() { done } +certsGenerateCCaaS() { + local CONFIG_PATH=$1 + local CONTAINER_NAME=$2 + local ORG_DOMAIN=$3 + local CHAINCODE_NAME=$4 + local PEER_ADDRESS=$5 + + local OUTPUT_PATH="${CONFIG_PATH}ccaas/${CONTAINER_NAME}/tls" + mkdir -p "$OUTPUT_PATH" + + local CA_CERT="${CONFIG_PATH}peerOrganizations/${ORG_DOMAIN}/tlsca/tlsca.${ORG_DOMAIN}-cert.pem" + local CA_KEY="${CONFIG_PATH}peerOrganizations/${ORG_DOMAIN}/tlsca/priv-key.pem" + + echo "Generating TLS certs for ${CONTAINER_NAME}..." + inputLog "CONFIG_PATH: $CONFIG_PATH" + inputLog "CONTAINER_NAME: $CONTAINER_NAME" + inputLog "ORG_DOMAIN: $ORG_DOMAIN" + inputLog "CHAINCODE_NAME: $CHAINCODE_NAME" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CA cert: $CA_CERT" + inputLog "CA key : $CA_KEY" + inputLog "OUTPUT_PATH: $OUTPUT_PATH" + + docker run --rm \ + -v "$OUTPUT_PATH:/certs" \ + -v "$CA_CERT:/ca/ca.crt:ro" \ + -v "$CA_KEY:/ca/ca.key:ro" \ + alpine:latest sh -c ' + apk add --no-cache openssl >/dev/null && + openssl genrsa -out /certs/client.key 2048 && + + # Create openssl config with SANs + cat > /certs/openssl.cnf << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +CN = '"$CONTAINER_NAME"' + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = '"$CONTAINER_NAME"' +DNS.2 = localhost +IP.1 = 127.0.0.1 +EOF + + openssl req -new -key /certs/client.key -out /certs/client.csr -config /certs/openssl.cnf && + openssl x509 -req -in /certs/client.csr -CA /ca/ca.crt -CAkey /ca/ca.key -CAcreateserial \ + -out /certs/client.crt -days 365 -sha256 -extensions v3_req -extfile /certs/openssl.cnf && + base64 /certs/client.crt > /certs/client_pem.crt && + base64 /certs/client.key > /certs/client_pem.key && + chown -R '"$(id -u):$(id -g)"' /certs + ' + + cp "$CA_CERT" "$OUTPUT_PATH/peer.crt" + echo "TLS certs generated for ${CONTAINER_NAME} at ${OUTPUT_PATH}" +} + genesisBlockCreate() { local CONTAINER_NAME=genesisBlockCreate diff --git a/src/setup-docker/templates/fabric-docker/scripts/chaincode-functions-v2.sh b/src/setup-docker/templates/fabric-docker/scripts/chaincode-functions-v2.sh index f35d4c26d..c1d62ea00 100644 --- a/src/setup-docker/templates/fabric-docker/scripts/chaincode-functions-v2.sh +++ b/src/setup-docker/templates/fabric-docker/scripts/chaincode-functions-v2.sh @@ -9,29 +9,27 @@ dockerPullIfMissing() { fi } -node_version_check(){ - - local fabric_shim_version="$1" - local nodejs_version +node_version_check() { + local fabric_shim_version="$1" + local nodejs_version - if [[ "$fabric_shim_version" == *"1.4."* ]]; then - nodejs_version=8.9 + if [[ "$fabric_shim_version" == *"1.4."* ]]; then + nodejs_version=8.9 - elif [[ "$fabric_shim_version" == *"2.2."* || "$fabric_shim_version" == *"2.3."* ]]; then - nodejs_version=12.13 + elif [[ "$fabric_shim_version" == *"2.2."* || "$fabric_shim_version" == *"2.3."* ]]; then + nodejs_version=12.13 - elif [[ "$fabric_shim_version" == *"2.4."* ]]; then - nodejs_version=16.16 + elif [[ "$fabric_shim_version" == *"2.4."* ]]; then + nodejs_version=16.16 - elif [[ "$fabric_shim_version" == *"2.5."* ]]; then - nodejs_version=18.12 + elif [[ "$fabric_shim_version" == *"2.5."* ]]; then + nodejs_version=18.12 - else - nodejs_version=18.12 - fi - - echo $nodejs_version + else + nodejs_version=18.12 + fi + echo $nodejs_version } chaincodeBuild() { @@ -73,10 +71,9 @@ chaincodeBuild() { inputLog "CHAINCODE_LANG: $CHAINCODE_LANG" inputLog "CHAINCODE_DIR_PATH: $CHAINCODE_DIR_PATH" inputLog "NODE_VERSION: $NODE_VERSION (recommended: $RECOMMENDED_NODE_VERSION)" - + # Default to using npm for installation and build (cd "$CHAINCODE_DIR_PATH" && npm install && npm run build) - fi } @@ -104,6 +101,70 @@ chaincodePackage() { docker exec "$CLI_NAME" chown "$(id -u):$(id -g)" "/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" } +chaincodePackageCCaaS() { + local CLI_NAME=$1 + local PEER_ADDRESS=$2 + local CHAINCODE_NAME=$3 + local CHAINCODE_VERSION=$4 + local CHAINCODE_LABEL="${CHAINCODE_NAME}_$CHAINCODE_VERSION" + local CHAINCODE_IMAGE=$5 + local CONTAINER_PORT=$6 + local CONTAINER_NAME=$7 + local TLS_ENABLED=$8 + + echo "Packaging CCaaS chaincode $CHAINCODE_NAME..." + inputLog "CHAINCODE_VERSION: $CHAINCODE_VERSION" + inputLog "PEER_ADDRESS: $PEER_ADDRESS" + inputLog "CLI_NAME: $CLI_NAME" + inputLog "CHAINCODE_IMAGE: $CHAINCODE_IMAGE" + inputLog "CONTAINER_PORT: $CONTAINER_PORT" + inputLog "TLS_ENABLED: $TLS_ENABLED" + + # Use the same container name logic as startCCaaSContainer + local ACTUAL_CONTAINER_NAME="ccaas-${PEER_ADDRESS%%:*}-${CHAINCODE_NAME}" + local PACKAGE_DIR="./chaincode-packages/ccaas_$ACTUAL_CONTAINER_NAME" + + mkdir -p "$PACKAGE_DIR" + echo "{\"type\":\"ccaas\",\"label\":\"$CHAINCODE_LABEL\"}" >"$PACKAGE_DIR/metadata.json" + + mkdir -p "$PACKAGE_DIR/code" + + if [ "$TLS_ENABLED" = true ]; then + # Use peer0.org1.example.com TLS certificates instead of CCaaS certificates + local PEER_TLS_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" + local ROOT_CERT=$(awk '{printf "%s\\n", $0}' "$PEER_TLS_PATH/ca.crt") + local SERVER_CERT=$(awk '{printf "%s\\n", $0}' "$PEER_TLS_PATH/server.crt") + local SERVER_KEY=$(awk '{printf "%s\\n", $0}' "$PEER_TLS_PATH/server.key") + + echo "{ + \"address\": \"${ACTUAL_CONTAINER_NAME}:7052\", + \"domain\": \"${ACTUAL_CONTAINER_NAME}\", + \"dial_timeout\": \"10s\", + \"tls_required\": $TLS_ENABLED, + \"client_auth_required\": true, + \"client_cert\": \"$SERVER_CERT\", + \"client_key\": \"$SERVER_KEY\", + \"root_cert\": \"$ROOT_CERT\" + }" >"$PACKAGE_DIR/code/connection.json" + else + echo "{ + \"address\": \"${ACTUAL_CONTAINER_NAME}:7052\", + \"dial_timeout\": \"10s\", + \"tls_required\": $TLS_ENABLED + }" >"$PACKAGE_DIR/code/connection.json" + fi + + tar -czf "$PACKAGE_DIR/code.tar.gz" -C "$PACKAGE_DIR/code" connection.json + tar -czf "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" -C "$PACKAGE_DIR" metadata.json code.tar.gz + + docker cp "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" "$CLI_NAME:/var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" + + rm "./chaincode-packages/$CHAINCODE_LABEL.tar.gz" + rm -rf "$PACKAGE_DIR" + + echo "CCaaS package created at /var/hyperledger/cli/chaincode-packages/$CHAINCODE_LABEL.tar.gz" +} + chaincodeInstall() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -127,6 +188,82 @@ chaincodeInstall() { "${CA_CERT_PARAMS[@]+"${CA_CERT_PARAMS[@]}"}" } +startCCaaSContainer() { + local PEER_ADDRESS="$1" + local CHAINCODE_NAME="$2" + local CHAINCODE_LABEL="$3" + local CHAINCODE_IMAGE="$4" + local EXTERNAL_PORT="$5" + local CLI_NAME="$6" + local CA_CERT="$7" + + local CONTAINER_NAME="ccaas-${PEER_ADDRESS%%:*}-${CHAINCODE_NAME}" + + # Query installed chaincodes to get the package ID + local CA_CERT_PARAMS=() + if [ -n "$CA_CERT" ]; then + CA_CERT_PARAMS=(--tls --cafile "/var/hyperledger/cli/$CA_CERT") + fi + + local QUERYINSTALLED_RESPONSE + local PACKAGE_ID + + QUERYINSTALLED_RESPONSE="$( + docker exec -e CORE_PEER_ADDRESS="$PEER_ADDRESS" "$CLI_NAME" peer lifecycle chaincode queryinstalled \ + --output json \ + "${CA_CERT_PARAMS[@]+"${CA_CERT_PARAMS[@]}"}" + )" + PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\"$CHAINCODE_LABEL\") ][0].package_id // \"\"" -r <<<"$QUERYINSTALLED_RESPONSE")" + + if [ -z "$PACKAGE_ID" ]; then + echo "ERROR: Package ID not found for chaincode $CHAINCODE_LABEL" + echo "QUERYINSTALLED_RESPONSE: $QUERYINSTALLED_RESPONSE" + exit 1 + fi + + echo "PACKAGE_ID: $PACKAGE_ID" + + local PACKAGE_HASH="${PACKAGE_ID#*:}" + + echo "Starting CCaaS container: $CONTAINER_NAME with ID: ${CHAINCODE_LABEL}:${PACKAGE_HASH}" + + # Extract peer name and organization domain from peer address + local PEER_NAME="${PEER_ADDRESS%%:*}" + + local ORG_DOMAIN=$(echo "$PEER_NAME" | sed 's/^[^.]*\.//') + local CONFIG_PATH="$FABLO_NETWORK_ROOT/fabric-config/crypto-config/" + local PORT_MAP="${EXTERNAL_PORT}:7052" + + local NETWORK=$(docker inspect "${PEER_ADDRESS%%:*}" | jq -r '.[0].NetworkSettings.Networks | keys[]') + + # Generate CCAAS-specific certificates with correct CN + echo "Generating CCAAS certificates for $CONTAINER_NAME..." + certsGenerateCCaaS "$CONFIG_PATH" "$CONTAINER_NAME" "$ORG_DOMAIN" "$CHAINCODE_NAME" "$PEER_ADDRESS" + + # Use generated CCAAS certificates + local CCAAS_TLS_PATH="$CONFIG_PATH/ccaas/$CONTAINER_NAME/tls" + + docker run -d \ + --name "$CONTAINER_NAME" \ + -e CORE_CHAINCODE_ADDRESS="0.0.0.0:7052" \ + -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:7052 \ + -e CORE_CHAINCODE_ID_NAME="${CHAINCODE_LABEL}:${PACKAGE_HASH}" \ + -e CHAINCODE_ID="${CHAINCODE_LABEL}:${PACKAGE_HASH}" \ + -e CORE_CHAINCODE_LOGGING_LEVEL=info \ + -e CORE_CHAINCODE_LOGGING_SHIM=info \ + -e CORE_PEER_TLS_ENABLED=true \ + -e CORE_CHAINCODE_TLS_CERT_FILE=/etc/hyperledger/fabric/client.crt \ + -e CORE_CHAINCODE_TLS_KEY_FILE=/etc/hyperledger/fabric/client.key \ + -e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt \ + -e CORE_PEER_LOCALMSPID=Org1MSP \ + -v "$CCAAS_TLS_PATH/client.key:/etc/hyperledger/fabric/client.key" \ + -v "$CCAAS_TLS_PATH/client.crt:/etc/hyperledger/fabric/client.crt" \ + -v "$CCAAS_TLS_PATH/peer.crt:/etc/hyperledger/fabric/peer.crt" \ + -p "$PORT_MAP" \ + --network "$NETWORK" \ + "$CHAINCODE_IMAGE" +} + chaincodeApprove() { local CLI_NAME=$1 local PEER_ADDRESS=$2 @@ -182,6 +319,7 @@ chaincodeApprove() { )" CC_PACKAGE_ID="$(jq ".installed_chaincodes | [.[]? | select(.label==\"$CHAINCODE_LABEL\") ][0].package_id // \"\"" -r <<<"$QUERYINSTALLED_RESPONSE")" if [ -z "$CC_PACKAGE_ID" ]; then + echo "CC_PACKAGE_ID not found, using default: $CHAINCODE_NAME:$CHAINCODE_VERSION" CC_PACKAGE_ID="$CHAINCODE_NAME:$CHAINCODE_VERSION" fi inputLog "CC_PACKAGE_ID: $CC_PACKAGE_ID" diff --git a/src/types/FabloConfigExtended.ts b/src/types/FabloConfigExtended.ts index 65de5bc14..d4ab6900e 100644 --- a/src/types/FabloConfigExtended.ts +++ b/src/types/FabloConfigExtended.ts @@ -145,12 +145,20 @@ export interface OrgConfig { tools: { fabloRest?: FabloRestConfig; explorer?: ExplorerConfig }; } +export interface peerChaincodeInstances { + containerName: string; + peerAddress: string; + port: number; +} export interface ChaincodeConfig { - directory: string; + directory?: string; name: string; version: string; lang: string; channel: ChannelConfig; + image?: string; + port?: number; + peerChaincodeInstances?: peerChaincodeInstances[]; init?: string; initRequired?: boolean; endorsement?: string; diff --git a/src/types/FabloConfigJson.ts b/src/types/FabloConfigJson.ts index bc50f916b..d43e78bde 100644 --- a/src/types/FabloConfigJson.ts +++ b/src/types/FabloConfigJson.ts @@ -54,12 +54,14 @@ export interface PrivateDataJson { export interface ChaincodeJson { name: string; version: string; - lang: "node" | "java" | "golang"; + lang: "node" | "java" | "golang" | "ccaas"; channel: string; init?: string; initRequired?: boolean; endorsement?: string; - directory: string; + directory?: string; + image?: string; + port?: number; privateData: PrivateDataJson[]; } diff --git a/src/validate/index.ts b/src/validate/index.ts index e4cd2ed19..ed6e25e62 100644 --- a/src/validate/index.ts +++ b/src/validate/index.ts @@ -74,6 +74,8 @@ class ValidateGenerator extends Generator { }); this.addListener(validationErrorType.CRITICAL, (event) => { + console.log(chalk.bold.bgRed("Critical error occured:")); + console.log(chalk.bold(`- ${event.message}`)); console.log(chalk.bold.bgRed("Critical error occured:")); console.log(chalk.bold(`- ${event.message}`)); this._printIfNotEmpty(this.errors.getAllMessages(), chalk.red.bold("Errors found:")); @@ -91,6 +93,7 @@ class ValidateGenerator extends Generator { const networkConfig = parseFabloConfig(this.fs.read(this.options.fabloConfigPath)); this._validateFabricVersion(networkConfig.global); + networkConfig.chaincodes.forEach((chaincode) => this._validateCcaaTLS(networkConfig.global, chaincode)); this._validateJsonSchema(networkConfig); this._validateSupportedFabloVersion(networkConfig.$schema); this._validateOrgs(networkConfig.orgs); @@ -99,6 +102,7 @@ class ValidateGenerator extends Generator { // === Validate Orderers ============= this._validateIfOrdererDefinitionExists(networkConfig.orgs); networkConfig.orgs.forEach((org) => this._validateOrdererCountForSoloType(org.orderers, networkConfig.global)); + networkConfig.orgs.forEach((org) => this._validateOrdererCountForSoloType(org.orderers, networkConfig.global)); networkConfig.orgs.forEach((org) => this._validateOrdererForRaftType(org.orderers, networkConfig.global)); networkConfig.orgs.forEach((org) => this._validateOrdererCountForOrg(org)); networkConfig.orgs.forEach((org) => this._validateOrdererGroupNameUniqueForOrg(org)); @@ -134,6 +138,16 @@ class ValidateGenerator extends Generator { } } + private _validateCcaaTLS(global: GlobalJson, chaincode: ChaincodeJson) { + if (chaincode.lang === "ccaas" && !global.tls) { + const objectToEmit = { + category: validationCategories.CRITICAL, + message: `Chaincode '${chaincode.name}' is using CCAAS, but TLS is disabled in the network. CCAAS with no TLS is not supported yet.`, + }; + this.emit(validationErrorType.CRITICAL, objectToEmit); + } + } + async shortSummary() { console.log(`Validation errors count: ${this.errors.count()}`); console.log(`Validation warnings count: ${this.warnings.count()}`);