├── js ├── .npmrc ├── .gitignore ├── tsconfig.json ├── rollup.config.js ├── app.json ├── package.json ├── src │ └── app.ts └── build_bundle.js ├── .gitignore ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── cpp ├── oe_sign.conf ├── CMakeLists.txt └── app │ └── app.cpp ├── .github ├── CODE_OF_CONDUCT.md ├── dependabot.yml ├── CONTRIBUTING.md └── workflows │ └── ci.yml ├── docker ├── ccf_app_js.enclave ├── ccf_app_js.virtual ├── ccf_app_cpp.enclave └── ccf_app_cpp.virtual ├── config ├── cchost_config_enclave_cpp.json ├── cchost_config_enclave_js.json ├── cchost_config_virtual_cpp.json └── cchost_config_virtual_js.json ├── LICENSE ├── SECURITY.md ├── .clang-format ├── .tours └── sample-cpp-app.tour └── README.md /js/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true -------------------------------------------------------------------------------- /js/.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules/ 3 | dist/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/build/** 2 | *.deb 3 | **/.venv_ccf_sandbox/ 4 | **/workspace/ -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/microsoft/ccf/app/dev/virtual:ccf-5.0.0 2 | 3 | RUN curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - && sudo apt-get install -y nodejs 4 | -------------------------------------------------------------------------------- /cpp/oe_sign.conf: -------------------------------------------------------------------------------- 1 | # Enclave settings: 2 | NumHeapPages=50000 3 | NumStackPages=1024 4 | NumTCS=8 5 | ProductID=1 6 | SecurityVersion=1 7 | # The Debug setting is automatically inserted by sign_app_library in CMake, to build both debuggable and non-debuggable variants -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Development Container for CCF C++ Apps", 3 | "dockerFile": "Dockerfile", 4 | "extensions": [ 5 | "ms-vscode.cpptools", 6 | "vsls-contrib.codetour" 7 | ], 8 | "features": { 9 | "ghcr.io/devcontainers/features/docker-from-docker:1": { 10 | "version": "20.10.8" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 5 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 6 | -------------------------------------------------------------------------------- /js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2020"], 4 | "target": "ES2020", 5 | "module": "ES2020", 6 | "moduleResolution": "node", 7 | "allowSyntheticDefaultImports": true, 8 | "noImplicitAny": false, 9 | "removeComments": true, 10 | "preserveConstEnums": true, 11 | "sourceMap": false 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/js" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /js/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import typescript from "@rollup/plugin-typescript"; 4 | 5 | export default { 6 | input: "src/app.ts", 7 | output: { 8 | dir: "dist/src", 9 | format: "es", 10 | preserveModules: true, 11 | preserveModulesRoot: "src", 12 | }, 13 | plugins: [ 14 | nodeResolve(), 15 | typescript({ compilerOptions: { noEmitOnError: true } }), 16 | commonjs(), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /js/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": { 3 | "/log": { 4 | "get": { 5 | "js_module": "app.js", 6 | "js_function": "read", 7 | "forwarding_required": "sometimes", 8 | "authn_policies": [], 9 | "mode": "readonly", 10 | "openapi": {} 11 | }, 12 | "post": { 13 | "js_module": "app.js", 14 | "js_function": "write", 15 | "forwarding_required": "sometimes", 16 | "authn_policies": [], 17 | "mode": "readwrite", 18 | "openapi": {} 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "del-cli -f dist/ && rollup --config && cp app.json dist/ && node build_bundle.js dist/", 5 | "bundle": "node build_bundle.js dist", 6 | "test": "node --version" 7 | }, 8 | "type": "module", 9 | "engines": { 10 | "node": ">=14" 11 | }, 12 | "dependencies": { 13 | "@microsoft/ccf-app": "^5.0.0" 14 | }, 15 | "devDependencies": { 16 | "@rollup/plugin-commonjs": "^26.0.1", 17 | "@rollup/plugin-node-resolve": "^15.2", 18 | "@rollup/plugin-typescript": "^11.1.6", 19 | "del-cli": "^5.1.0", 20 | "rollup": "^2.41.0", 21 | "tslib": "^2.6.3", 22 | "typescript": "^5.4.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docker/ccf_app_js.enclave: -------------------------------------------------------------------------------- 1 | # Build 2 | FROM ghcr.io/microsoft/ccf/app/dev/sgx:ccf-5.0.0 as builder 3 | 4 | # Run 5 | FROM ghcr.io/microsoft/ccf/app/run-js/sgx:ccf-5.0.0 6 | 7 | COPY --from=builder /opt/ccf_sgx/bin/*.js /app/ 8 | COPY --from=builder /opt/ccf_sgx/bin/keygenerator.sh /app/ 9 | COPY ./config/cchost_config_enclave_js.json /app/ 10 | WORKDIR /app/ 11 | # IMPORTANT: This creates a member key pair and stores it in the container, 12 | # which is only acceptable for test purposes. A real deployment must instead 13 | # create and store keys somewhere secure, and only load the public key in the 14 | # container, preferably through a mount. 15 | RUN /app/keygenerator.sh --name member0 --gen-enc-key 16 | 17 | EXPOSE 8080/tcp 18 | 19 | CMD ["/usr/bin/cchost", "--config", "/app/cchost_config_enclave_js.json"] 20 | -------------------------------------------------------------------------------- /docker/ccf_app_js.virtual: -------------------------------------------------------------------------------- 1 | # Build 2 | FROM ghcr.io/microsoft/ccf/app/dev/virtual:ccf-5.0.0 as builder 3 | 4 | # Run 5 | FROM ghcr.io/microsoft/ccf/app/run-js/virtual:ccf-5.0.0 6 | 7 | COPY --from=builder /opt/ccf_virtual/bin/*.js /app/ 8 | COPY --from=builder /opt/ccf_virtual/bin/keygenerator.sh /app/ 9 | COPY ./config/cchost_config_virtual_js.json /app/ 10 | WORKDIR /app/ 11 | # IMPORTANT: This creates a member key pair and stores it in the container, 12 | # which is only acceptable for test purposes. A real deployment must instead 13 | # create and store keys somewhere secure, and only load the public key in the 14 | # container, preferably through a mount. 15 | RUN /app/keygenerator.sh --name member0 --gen-enc-key 16 | 17 | EXPOSE 8080/tcp 18 | 19 | CMD ["/usr/bin/cchost", "--config", "/app/cchost_config_virtual_js.json"] 20 | -------------------------------------------------------------------------------- /config/cchost_config_enclave_cpp.json: -------------------------------------------------------------------------------- 1 | { 2 | "enclave": { 3 | "file": "/app/libccf_app.enclave.so.signed", 4 | "type": "Release", 5 | "platform": "SGX" 6 | }, 7 | "network": { 8 | "node_to_node_interface": { "bind_address": "127.0.0.1:8081" }, 9 | "rpc_interfaces": { 10 | "main_interface": { 11 | "bind_address": "0.0.0.0:8080" 12 | } 13 | } 14 | }, 15 | "command": { 16 | "type": "Start", 17 | "service_certificate_file": "/app/service_cert.pem", 18 | "start": { 19 | "constitution_files": [ 20 | "/app/validate.js", 21 | "/app/apply.js", 22 | "/app/resolve.js", 23 | "/app/actions.js" 24 | ], 25 | "members": [ 26 | { 27 | "certificate_file": "/app/member0_cert.pem", 28 | "encryption_public_key_file": "/app/member0_enc_pubk.pem" 29 | } 30 | ] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /config/cchost_config_enclave_js.json: -------------------------------------------------------------------------------- 1 | { 2 | "enclave": { 3 | "file": "/usr/lib/ccf/libjs_generic.enclave.so.signed", 4 | "type": "Release", 5 | "platform": "SGX" 6 | }, 7 | "network": { 8 | "node_to_node_interface": { "bind_address": "127.0.0.1:8081" }, 9 | "rpc_interfaces": { 10 | "main_interface": { 11 | "bind_address": "0.0.0.0:8080" 12 | } 13 | } 14 | }, 15 | "command": { 16 | "type": "Start", 17 | "service_certificate_file": "/app/service_cert.pem", 18 | "start": { 19 | "constitution_files": [ 20 | "/app/validate.js", 21 | "/app/apply.js", 22 | "/app/resolve.js", 23 | "/app/actions.js" 24 | ], 25 | "members": [ 26 | { 27 | "certificate_file": "/app/member0_cert.pem", 28 | "encryption_public_key_file": "/app/member0_enc_pubk.pem" 29 | } 30 | ] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 10 | 11 | Note that we only accept pull requests from forks so please fork this repository before making any changes. You should contribute your changes on a branch on that fork and create a pull request on the [microsoft/ccf_app_template repository](https://github.com/microsoft/ccf_app_template/compare) from there. 12 | -------------------------------------------------------------------------------- /cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | 6 | project(ccf_app LANGUAGES C CXX) 7 | 8 | option( 9 | COMPILE_TARGET 10 | "Compile target to build for, one of [virtual;sgx;snp], defaults to virtual" 11 | virtual) 12 | 13 | find_package("ccf_${COMPILE_TARGET}" REQUIRED) 14 | 15 | add_ccf_app(ccf_app SRCS ${CMAKE_CURRENT_SOURCE_DIR}/app/app.cpp) 16 | 17 | # Generate an ephemeral signing key 18 | add_custom_command( 19 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/signing_key.pem 20 | COMMAND openssl genrsa -out ${CMAKE_CURRENT_BINARY_DIR}/signing_key.pem -3 21 | 3072 22 | ) 23 | add_custom_target( 24 | app_signing_key ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/signing_key.pem 25 | ) 26 | 27 | sign_app_library( 28 | ccf_app.enclave ${CMAKE_CURRENT_SOURCE_DIR}/oe_sign.conf 29 | ${CMAKE_CURRENT_BINARY_DIR}/signing_key.pem 30 | ) -------------------------------------------------------------------------------- /config/cchost_config_virtual_cpp.json: -------------------------------------------------------------------------------- 1 | { 2 | "enclave": { 3 | "file": "/app/libccf_app.virtual.so", 4 | "type": "Virtual", 5 | "platform": "Virtual" 6 | }, 7 | "network": { 8 | "node_to_node_interface": { "bind_address": "127.0.0.1:8081" }, 9 | "rpc_interfaces": { 10 | "main_interface": { 11 | "bind_address": "0.0.0.0:8080" 12 | } 13 | } 14 | }, 15 | "command": { 16 | "type": "Start", 17 | "service_certificate_file": "/app/service_cert.pem", 18 | "start": { 19 | "constitution_files": [ 20 | "/app/validate.js", 21 | "/app/apply.js", 22 | "/app/resolve.js", 23 | "/app/actions.js" 24 | ], 25 | "members": [ 26 | { 27 | "certificate_file": "/app/member0_cert.pem", 28 | "encryption_public_key_file": "/app/member0_enc_pubk.pem" 29 | } 30 | ] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /config/cchost_config_virtual_js.json: -------------------------------------------------------------------------------- 1 | { 2 | "enclave": { 3 | "file": "/usr/lib/ccf/libjs_generic.virtual.so", 4 | "type": "Virtual", 5 | "platform": "Virtual" 6 | }, 7 | "network": { 8 | "node_to_node_interface": { "bind_address": "127.0.0.1:8081" }, 9 | "rpc_interfaces": { 10 | "main_interface": { 11 | "bind_address": "0.0.0.0:8080" 12 | } 13 | } 14 | }, 15 | "command": { 16 | "type": "Start", 17 | "service_certificate_file": "/app/service_cert.pem", 18 | "start": { 19 | "constitution_files": [ 20 | "/app/validate.js", 21 | "/app/apply.js", 22 | "/app/resolve.js", 23 | "/app/actions.js" 24 | ], 25 | "members": [ 26 | { 27 | "certificate_file": "/app/member0_cert.pem", 28 | "encryption_public_key_file": "/app/member0_enc_pubk.pem" 29 | } 30 | ] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /docker/ccf_app_cpp.enclave: -------------------------------------------------------------------------------- 1 | # Build 2 | FROM ghcr.io/microsoft/ccf/app/dev/sgx:ccf-5.0.0 as builder 3 | COPY ./cpp /cpp 4 | RUN mkdir -p /build/ 5 | WORKDIR /build/ 6 | RUN CC="clang-11" CXX="clang++-11" cmake -DCOMPILE_TARGET=sgx -GNinja /cpp && ninja 7 | 8 | # Run 9 | FROM ghcr.io/microsoft/ccf/app/run/sgx:ccf-5.0.0 10 | 11 | COPY --from=builder /build/libccf_app.enclave.so.signed /app/ 12 | COPY --from=builder /opt/ccf_sgx/bin/*.js /app/ 13 | COPY --from=builder /opt/ccf_sgx/bin/keygenerator.sh /app/ 14 | COPY ./config/cchost_config_enclave_cpp.json /app/ 15 | WORKDIR /app/ 16 | # IMPORTANT: This creates a member key pair and stores it in the container, 17 | # which is only acceptable for test purposes. A real deployment must instead 18 | # create and store keys somewhere secure, and only load the public key in the 19 | # container, preferably through a mount. 20 | RUN /app/keygenerator.sh --name member0 --gen-enc-key 21 | 22 | EXPOSE 8080/tcp 23 | 24 | CMD ["/usr/bin/cchost", "--config", "/app/cchost_config_enclave_cpp.json"] 25 | -------------------------------------------------------------------------------- /docker/ccf_app_cpp.virtual: -------------------------------------------------------------------------------- 1 | # Build 2 | FROM ghcr.io/microsoft/ccf/app/dev/virtual:ccf-5.0.0 as builder 3 | COPY ./cpp /cpp 4 | RUN mkdir -p /build/ 5 | WORKDIR /build/ 6 | RUN CC="clang-15" CXX="clang++-15" cmake -GNinja -DCOMPILE_TARGET=virtual /cpp && ninja 7 | 8 | # Run 9 | FROM ghcr.io/microsoft/ccf/app/run/virtual:ccf-5.0.0 10 | 11 | COPY --from=builder /build/libccf_app.virtual.so /app/ 12 | COPY --from=builder /opt/ccf_virtual/bin/*.js /app/ 13 | COPY --from=builder /opt/ccf_virtual/bin/keygenerator.sh /app/ 14 | COPY ./config/cchost_config_virtual_cpp.json /app/ 15 | WORKDIR /app/ 16 | # IMPORTANT: This creates a member key pair and stores it in the container, 17 | # which is only acceptable for test purposes. A real deployment must instead 18 | # create and store keys somewhere secure, and only load the public key in the 19 | # container, preferably through a mount. 20 | RUN /app/keygenerator.sh --name member0 --gen-enc-key 21 | 22 | EXPOSE 8080/tcp 23 | 24 | CMD ["/usr/bin/cchost", "--config", "/app/cchost_config_virtual_cpp.json"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /js/src/app.ts: -------------------------------------------------------------------------------- 1 | import * as ccfapp from "@microsoft/ccf-app"; 2 | 3 | function parse_request_query(request: ccfapp.Request): { 4 | [key: string]: string; 5 | } { 6 | const elements = request.query.split("&"); 7 | const obj = {}; 8 | for (const kv of elements) { 9 | const [k, v] = kv.split("="); 10 | obj[k] = v; 11 | } 12 | return obj; 13 | } 14 | 15 | const recordsMap = ccfapp.typedKv("records", ccfapp.string, ccfapp.string); 16 | 17 | export function write(request: ccfapp.Request): ccfapp.Response { 18 | const parsedQuery = parse_request_query(request); 19 | if (parsedQuery.id === undefined) { 20 | return { body: { error: "Missing query parameter 'id'" } }; 21 | } 22 | const params = request.body.json(); 23 | 24 | recordsMap.set(parsedQuery.id, params.msg); 25 | return {}; 26 | } 27 | 28 | export function read(request: ccfapp.Request): ccfapp.Response { 29 | const parsedQuery = parse_request_query(request); 30 | if (parsedQuery.id === undefined) { 31 | return { body: { error: "Missing query parameter 'id'" } }; 32 | } 33 | 34 | const msg = recordsMap.get(parsedQuery.id); 35 | if (msg === undefined) { 36 | return { 37 | body: { error: `Cannot find record for id \"${parsedQuery.id}\".` }, 38 | }; 39 | } 40 | return { body: msg }; 41 | } 42 | -------------------------------------------------------------------------------- /js/build_bundle.js: -------------------------------------------------------------------------------- 1 | import { readdirSync, statSync, readFileSync, writeFileSync } from "fs"; 2 | import { join, posix, sep } from "path"; 3 | 4 | const args = process.argv.slice(2); 5 | 6 | const getAllFiles = function (dirPath, arrayOfFiles) { 7 | arrayOfFiles = arrayOfFiles || []; 8 | 9 | const files = readdirSync(dirPath); 10 | for (const file of files) { 11 | const filePath = join(dirPath, file); 12 | if (statSync(filePath).isDirectory()) { 13 | arrayOfFiles = getAllFiles(filePath, arrayOfFiles); 14 | } else { 15 | arrayOfFiles.push(filePath); 16 | } 17 | } 18 | 19 | return arrayOfFiles; 20 | }; 21 | 22 | const removePrefix = function (s, prefix) { 23 | return s.substr(prefix.length).split(sep).join(posix.sep); 24 | }; 25 | 26 | const rootDir = args[0]; 27 | 28 | const metadataPath = join(rootDir, "app.json"); 29 | const metadata = JSON.parse(readFileSync(metadataPath, "utf-8")); 30 | 31 | const srcDir = join(rootDir, "src"); 32 | const allFiles = getAllFiles(srcDir); 33 | 34 | // The trailing / is included so that it is trimmed in removePrefix. 35 | // This produces "foo/bar.js" rather than "/foo/bar.js" 36 | const toTrim = srcDir + "/"; 37 | 38 | const modules = allFiles.map(function (filePath) { 39 | return { 40 | name: removePrefix(filePath, toTrim), 41 | module: readFileSync(filePath, "utf-8"), 42 | }; 43 | }); 44 | 45 | const bundlePath = join(args[0], "bundle.json"); 46 | const bundle = { 47 | metadata: metadata, 48 | modules: modules, 49 | }; 50 | console.log( 51 | `Writing bundle containing ${modules.length} modules to ${bundlePath}` 52 | ); 53 | writeFileSync(bundlePath, JSON.stringify(bundle)); 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "CCF App Template CI" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build-cpp-app-sgx: 11 | runs-on: ubuntu-20.04 12 | container: ghcr.io/microsoft/ccf/app/dev/sgx:ccf-5.0.0 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Build app (SGX) 19 | run: mkdir -p build && cd build && CC="clang-11" CXX="clang++-11" cmake -GNinja -DCOMPILE_TARGET=sgx .. && ninja 20 | working-directory: cpp 21 | 22 | build-cpp-app-virtual: 23 | runs-on: ubuntu-20.04 24 | container: ghcr.io/microsoft/ccf/app/dev/virtual:ccf-5.0.0 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v4 29 | 30 | - name: Build app (virtual) 31 | run: mkdir -p build && cd build && CC="$(which clang-15)" CXX="$(which clang++-15)" cmake -GNinja -DCOMPILE_TARGET=virtual .. && ninja 32 | working-directory: cpp 33 | 34 | build-js-app: 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: actions/setup-node@v4 38 | with: 39 | node-version: 18 40 | - name: Build js app 41 | run: npm install && npm run build 42 | working-directory: js 43 | 44 | build-containers: 45 | runs-on: ubuntu-20.04 46 | container: ghcr.io/microsoft/ccf/app/dev/sgx:ccf-5.0.0 47 | 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v4 51 | 52 | - name: Build C++ enclave container 53 | run: docker build -t ccf-app-template:cpp-enclave -f docker/ccf_app_cpp.enclave . 54 | 55 | - name: Build C++ virtual container 56 | run: docker build -t ccf-app-template:cpp-virtual -f docker/ccf_app_cpp.virtual . 57 | 58 | - name: Build JS enclave container 59 | run: docker build -t ccf-app-template:js-enclave -f docker/ccf_app_js.enclave . 60 | 61 | - name: Build JS virtual container 62 | run: docker build -t ccf-app-template:js-virtual -f docker/ccf_app_js.virtual . 63 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 4 | 5 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 6 | 7 | ## Reporting Security Issues 8 | 9 | **Please do not report security vulnerabilities through public GitHub issues.** 10 | 11 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 12 | 13 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 14 | 15 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 16 | 17 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 18 | 19 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 20 | * Full paths of source file(s) related to the manifestation of the issue 21 | * The location of the affected source code (tag/branch/commit or direct URL) 22 | * Any special configuration required to reproduce the issue 23 | * Step-by-step instructions to reproduce the issue 24 | * Proof-of-concept or exploit code (if possible) 25 | * Impact of the issue, including how an attacker might exploit the issue 26 | 27 | This information will help us triage your report more quickly. 28 | 29 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 30 | 31 | ## Preferred Languages 32 | 33 | We prefer all communications to be in English. 34 | 35 | ## Policy 36 | 37 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 38 | -------------------------------------------------------------------------------- /cpp/app/app.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "ccf/app_interface.h" 5 | #include "ccf/common_auth_policies.h" 6 | #include "ccf/http_query.h" 7 | #include "ccf/json_handler.h" 8 | 9 | #define FMT_HEADER_ONLY 10 | #include 11 | 12 | namespace app 13 | { 14 | // Key-value store types 15 | using Map = ccf::kv::Map; 16 | static constexpr auto RECORDS = "records"; 17 | 18 | // API types 19 | struct Write 20 | { 21 | struct In 22 | { 23 | std::string msg; 24 | }; 25 | 26 | using Out = void; 27 | }; 28 | DECLARE_JSON_TYPE(Write::In); 29 | DECLARE_JSON_REQUIRED_FIELDS(Write::In, msg); 30 | 31 | class AppHandlers : public ccf::UserEndpointRegistry 32 | { 33 | public: 34 | AppHandlers(ccf::AbstractNodeContext& context) : 35 | ccf::UserEndpointRegistry(context) 36 | { 37 | openapi_info.title = "CCF Sample C++ App"; 38 | openapi_info.description = 39 | "This minimal CCF C++ application aims to be " 40 | "used as a template for CCF developers."; 41 | openapi_info.document_version = "0.0.1"; 42 | 43 | auto write = [this](auto& ctx, nlohmann::json&& params) { 44 | const auto parsed_query = 45 | ccf::http::parse_query(ctx.rpc_ctx->get_request_query()); 46 | 47 | std::string error_reason; 48 | size_t id = 0; 49 | if (!ccf::http::get_query_value(parsed_query, "id", id, error_reason)) 50 | { 51 | return ccf::make_error( 52 | HTTP_STATUS_BAD_REQUEST, 53 | ccf::errors::InvalidQueryParameterValue, 54 | std::move(error_reason)); 55 | } 56 | 57 | const auto in = params.get(); 58 | if (in.msg.empty()) 59 | { 60 | return ccf::make_error( 61 | HTTP_STATUS_BAD_REQUEST, 62 | ccf::errors::InvalidInput, 63 | "Cannot record an empty log message."); 64 | } 65 | 66 | auto records_handle = ctx.tx.template rw(RECORDS); 67 | records_handle->put(id, in.msg); 68 | return ccf::make_success(); 69 | }; 70 | 71 | make_endpoint( 72 | "/log", HTTP_POST, ccf::json_adapter(write), ccf::no_auth_required) 73 | .set_auto_schema() 74 | .add_query_parameter("id") 75 | .install(); 76 | 77 | auto read = [this](auto& ctx, nlohmann::json&& params) { 78 | const auto parsed_query = 79 | ccf::http::parse_query(ctx.rpc_ctx->get_request_query()); 80 | 81 | std::string error_reason; 82 | size_t id = 0; 83 | if (!ccf::http::get_query_value(parsed_query, "id", id, error_reason)) 84 | { 85 | return ccf::make_error( 86 | HTTP_STATUS_BAD_REQUEST, 87 | ccf::errors::InvalidQueryParameterValue, 88 | std::move(error_reason)); 89 | } 90 | 91 | auto records_handle = ctx.tx.template ro(RECORDS); 92 | auto msg = records_handle->get(id); 93 | if (!msg.has_value()) 94 | { 95 | return ccf::make_error( 96 | HTTP_STATUS_NOT_FOUND, 97 | ccf::errors::ResourceNotFound, 98 | fmt::format("Cannot find record for id \"{}\".", id)); 99 | } 100 | return ccf::make_success(msg.value()); 101 | }; 102 | 103 | make_read_only_endpoint( 104 | "/log", 105 | HTTP_GET, 106 | ccf::json_read_only_adapter(read), 107 | ccf::no_auth_required) 108 | .set_auto_schema() 109 | .add_query_parameter("id") 110 | .install(); 111 | } 112 | }; 113 | } // namespace app 114 | 115 | namespace ccf 116 | { 117 | std::unique_ptr make_user_endpoints( 118 | ccf::AbstractNodeContext& context) 119 | { 120 | return std::make_unique(context); 121 | } 122 | } // namespace ccf -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveMacros: false 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: DontAlign 9 | AlignOperands: false 10 | AlignTrailingComments: false 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: true 14 | AllowShortBlocksOnASingleLine: Never 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Empty 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterReturnType: None 21 | AlwaysBreakBeforeMultilineStrings: true 22 | AlwaysBreakTemplateDeclarations: Yes 23 | BinPackArguments: false 24 | BinPackParameters: false 25 | BraceWrapping: 26 | AfterCaseLabel: true 27 | AfterClass: true 28 | AfterControlStatement: true 29 | AfterEnum: true 30 | AfterFunction: true 31 | AfterNamespace: true 32 | AfterObjCDeclaration: true 33 | AfterStruct: true 34 | AfterUnion: true 35 | AfterExternBlock: true 36 | BeforeCatch: true 37 | BeforeElse: true 38 | IndentBraces: false 39 | SplitEmptyFunction: false 40 | SplitEmptyRecord: false 41 | SplitEmptyNamespace: false 42 | BreakBeforeBinaryOperators: None 43 | BreakBeforeBraces: Custom 44 | BreakInheritanceList: BeforeColon 45 | BreakBeforeTernaryOperators: false 46 | BreakConstructorInitializers: AfterColon 47 | BreakStringLiterals: true 48 | ColumnLimit: 80 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 52 | ConstructorInitializerIndentWidth: 2 53 | ContinuationIndentWidth: 2 54 | Cpp11BracedListStyle: true 55 | DeriveLineEnding: true 56 | DerivePointerAlignment: false 57 | DisableFormat: false 58 | ExperimentalAutoDetectBinPacking: false 59 | FixNamespaceComments: false 60 | ForEachMacros: 61 | - FOREACH 62 | - Q_FOREACH 63 | - BOOST_FOREACH 64 | IncludeBlocks: Regroup 65 | IncludeCategories: 66 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 67 | Priority: 2 68 | SortPriority: 0 69 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 70 | Priority: 3 71 | SortPriority: 0 72 | - Regex: '.*' 73 | Priority: 1 74 | SortPriority: 0 75 | IncludeIsMainRegex: '(Test)?$' 76 | IncludeIsMainSourceRegex: '' 77 | IndentCaseLabels: true 78 | IndentGotoLabels: true 79 | IndentPPDirectives: AfterHash 80 | IndentWidth: 2 81 | IndentWrappedFunctionNames: false 82 | JavaScriptQuotes: Leave 83 | JavaScriptWrapImports: true 84 | KeepEmptyLinesAtTheStartOfBlocks: false 85 | MacroBlockBegin: '' 86 | MacroBlockEnd: '' 87 | MaxEmptyLinesToKeep: 1 88 | NamespaceIndentation: All 89 | ObjCBinPackProtocolList: Auto 90 | ObjCBlockIndentWidth: 2 91 | ObjCSpaceAfterProperty: false 92 | ObjCSpaceBeforeProtocolList: true 93 | PenaltyBreakAssignment: 2 94 | PenaltyBreakBeforeFirstCallParameter: 19 95 | PenaltyBreakComment: 300 96 | PenaltyBreakFirstLessLess: 120 97 | PenaltyBreakString: 1000 98 | PenaltyBreakTemplateDeclaration: 10 99 | PenaltyExcessCharacter: 1000000 100 | PenaltyReturnTypeOnItsOwnLine: 600 101 | PointerAlignment: Left 102 | ReflowComments: true 103 | SortIncludes: true 104 | SortUsingDeclarations: true 105 | SpaceAfterCStyleCast: false 106 | SpaceAfterLogicalNot: false 107 | SpaceAfterTemplateKeyword: true 108 | SpaceBeforeAssignmentOperators: true 109 | SpaceBeforeCpp11BracedList: false 110 | SpaceBeforeCtorInitializerColon: true 111 | SpaceBeforeInheritanceColon: true 112 | SpaceBeforeParens: ControlStatements 113 | SpaceBeforeRangeBasedForLoopColon: true 114 | SpaceInEmptyBlock: false 115 | SpaceInEmptyParentheses: false 116 | SpacesBeforeTrailingComments: 1 117 | SpacesInAngles: false 118 | SpacesInConditionalStatement: false 119 | SpacesInContainerLiterals: false 120 | SpacesInCStyleCastParentheses: false 121 | SpacesInParentheses: false 122 | SpacesInSquareBrackets: false 123 | SpaceBeforeSquareBrackets: false 124 | Standard: c++20 125 | StatementMacros: 126 | - Q_UNUSED 127 | - QT_REQUIRE_VERSION 128 | TabWidth: 2 129 | UseCRLF: false 130 | UseTab: Never 131 | ... 132 | -------------------------------------------------------------------------------- /.tours/sample-cpp-app.tour: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://aka.ms/codetour-schema", 3 | "title": "Sample C++ App Tour", 4 | "steps": [ 5 | { 6 | "file": "cpp/app/app.cpp", 7 | "description": "This file defines a sample CCF C++ application. \n\nThe sample application is simple and does not make use of all CCF features (e.g. historical queries). However, it is a great starting point for new CCF developers.", 8 | "line": 1 9 | }, 10 | { 11 | "file": "cpp/app/app.cpp", 12 | "description": "Here, the application is instantiated. \n\nThis lets CCF know that the application handlers should be registered and available on each CCF node starting this application. ", 13 | "line": 120, 14 | "selection": { 15 | "start": { 16 | "line": 118, 17 | "character": 1 18 | }, 19 | "end": { 20 | "line": 125, 21 | "character": 22 22 | } 23 | } 24 | }, 25 | { 26 | "file": "cpp/app/app.cpp", 27 | "description": "The application logic is defined in this namespace.\n\nIn particular, it defines:\n1. The types used by the app to record its state in the key-value store.\n2. The API schema for each endpoint.\n3. The HTTP endpoints that contain the business logic of the application and that can be invoked by client requests.", 28 | "line": 12 29 | }, 30 | { 31 | "file": "cpp/app/app.cpp", 32 | "description": "The application makes use of a single key-value store map, which maps a `size_t` key to a `std::string` value (e.g. 0 -> \"hello world\").\n\nAll `put` operations on this map are recorded in the CCF ledger. ", 33 | "line": 15 34 | }, 35 | { 36 | "file": "cpp/app/app.cpp", 37 | "description": "This defines the name of the `kv::Map` declared above. \n\nNote that the name is *not* prefixed with `public:` so all data written to this map is encrypted in the ledger. If the table was prefixed with `public:`, all writes to the map would be recorded in clear in the ledger, which is useful for auditable public data.", 38 | "line": 16 39 | }, 40 | { 41 | "file": "cpp/app/app.cpp", 42 | "description": "CCF applications automatically generate their own OpenAPI specification (accessible via `GET /app/api`).\n\nThese lines specify metadata for the entire application.", 43 | "line": 37, 44 | "selection": { 45 | "start": { 46 | "line": 37, 47 | "character": 1 48 | }, 49 | "end": { 50 | "line": 41, 51 | "character": 47 52 | } 53 | } 54 | }, 55 | { 56 | "file": "cpp/app/app.cpp", 57 | "description": "This is the first HTTP handler defined by the application.\n\nIt lets users record an arbitrary message in the \"records\" private key-value map. The unique key of the message is set by the user as the `id` query parameter. \n\nEach handler is given a single transaction object (`ctx.tx`) that needs to be used to mutate and read from the key-value store.", 58 | "line": 43 59 | }, 60 | { 61 | "file": "cpp/app/app.cpp", 62 | "description": "This is the CCF key-value store API to write to a specific map. \n\nA \"handle\" should be first retrieved on the map, using the `rw(RECORDS)` method on the unique transaction object `ctx.tx`. Then, a value (`in.msg`) can be recorded at a specific key (`id`) using the `put(key, value)` call. \n\nSee https://microsoft.github.io/CCF/release/2.x/build_apps/kv/index.html for the full key-value store API.", 63 | "line": 66, 64 | "selection": { 65 | "start": { 66 | "line": 66, 67 | "character": 9 68 | }, 69 | "end": { 70 | "line": 69, 71 | "character": 41 72 | } 73 | } 74 | }, 75 | { 76 | "file": "cpp/app/app.cpp", 77 | "description": "This installs the `write` handler as an HTTP endpoint.\n\nIt specifies:\n- The URI of the HTTP endpoint. In this case, `POST /app/log`. Note that all CCF application endpoints are prefixed with `/app`.\n- The content type of the HTTP request and response, in this case JSON.\n- The user authentication scheme. In this case, unauthenticated users are allowed to invoke the endpoint. See https://microsoft.github.io/CCF/release/2.x/build_apps/auth/index.html for the list of all supported authentication schemes.\n- The API schema to use in the generated OpenAPI specification.\n- The `id` query parameter at which the user message will be recorded. ", 78 | "line": 71, 79 | "selection": { 80 | "start": { 81 | "line": 71, 82 | "character": 7 83 | }, 84 | "end": { 85 | "line": 75, 86 | "character": 20 87 | } 88 | } 89 | }, 90 | { 91 | "file": "cpp/app/app.cpp", 92 | "description": "This is the second handler for this sample application.\n\nIt is similar to the first handler except that it only reads from the key-value store.", 93 | "line": 77 94 | }, 95 | { 96 | "file": "cpp/app/app.cpp", 97 | "description": "Note that `make_read_only_endpoint` is used to specify that the endpoint does not write to the key-value store. \n\nThe endpoint is accessible at `GET /app/log` and as such, does not accept any request body.\n\n", 98 | "line": 103, 99 | "selection": { 100 | "start": { 101 | "line": 103, 102 | "character": 7 103 | }, 104 | "end": { 105 | "line": 110, 106 | "character": 20 107 | } 108 | } 109 | }, 110 | { 111 | "file": "cpp/app/app.cpp", 112 | "description": "That's it! \n\nFor more information on how to build CCF C++ applications, see https://microsoft.github.io/CCF/release/2.x/build_apps/example.html. \n\nFor any questions or bugs, please open an issue on Github: https://github.com/microsoft/CCF/issues/new/choose.", 113 | "line": 122 114 | }, 115 | { 116 | "file": "README.md", 117 | "description": "You can build the sample application by running the following steps.\n\nMake sure the dependencies have been installed, or that you have checked out this repository in a VSCode development container (see above).", 118 | "line": 59 119 | }, 120 | { 121 | "file": "README.md", 122 | "description": "Finally, you can run the application locally using the CCF sandbox script.\n\nYou can then interact with the application with `curl`.", 123 | "line": 75 124 | } 125 | ], 126 | "ref": "main" 127 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CCF App Template [![Open in VSCode](https://img.shields.io/static/v1?label=Open+in&message=VSCode&logo=visualstudiocode&color=007ACC&logoColor=007ACC&labelColor=2C2C32)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/ccf-app-template) 2 | 3 | [![CCF App Template CI](https://github.com/microsoft/ccf-app-template/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/ccf-app-template/actions/workflows/ci.yml) 4 | 5 | Template repository for JavaScript and C++ CCF applications. 6 | 7 | Note: For complete sample apps, see https://github.com/microsoft/ccf-app-samples. 8 | 9 | ## Quickstart 10 | 11 | The quickest way to build and run this sample CCF app is to checkout this repository locally in its development container by clicking: 12 | [![Open in VSCode](https://img.shields.io/static/v1?label=Open+in&message=VSCode&logo=visualstudiocode&color=007ACC&logoColor=007ACC&labelColor=2C2C32)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/ccf-app-template) 13 | 14 | All dependencies will be automatically installed (takes ~2 mins on first checkout). 15 | 16 | Alternatively, you can checkout this repository in a Github codespace: [![Open in Github codespace](https://img.shields.io/static/v1?label=Open+in&message=GitHub+codespace&logo=github&color=2F363D&logoColor=white&labelColor=2C2C32)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=496290904&machine=basicLinux32gb&devcontainer_path=.devcontainer.json&location=WestEurope) 17 | 18 | ## JavaScript 19 | 20 | CCF apps can be written in JavaScript/TypeScript. This is the quickest way to develop new apps as this does not require any compilation step and the app can be updated on the fly, via [a governance proposal](https://microsoft.github.io/CCF/main/build_apps/js_app_bundle.html#deployment). 21 | 22 | The JavaScript sample bundle is located in the [`js/`](js/) directory. 23 | 24 | ### Run JS app 25 | 26 | ```bash 27 | $ npm --prefix ./js install 28 | $ npm --prefix ./js run build 29 | $ /opt/ccf_virtual/bin/sandbox.sh --js-app-bundle ./js/dist/ 30 | [12:00:00.000] Virtual mode enabled 31 | [12:00:00.000] Starting 1 CCF node... 32 | [12:00:00.000] Started CCF network with the following nodes: 33 | [12:00:00.000] Node [0] = https://127.0.0.1:8000 34 | [12:00:00.000] You can now issue business transactions to the libjs_generic application 35 | [12:00:00.000] Loaded JS application: ./js/dist/ 36 | [12:00:00.000] Keys and certificates have been copied to the common folder: /workspaces/ccf-app-template/workspace/sandbox_common 37 | [12:00:00.000] See https://microsoft.github.io/CCF/main/use_apps/issue_commands.html for more information 38 | [12:00:00.000] Press Ctrl+C to shutdown the network 39 | ``` 40 | 41 | In another terminal: 42 | 43 | ```bash 44 | $ curl -X POST "https://127.0.0.1:8000/app/log?id=1" --cacert ./workspace/sandbox_common/service_cert.pem -H "Content-Type: application/json" --data '{"msg": "hello world"}' 45 | $ curl "https://127.0.0.1:8000/app/log?id=1" --cacert ./workspace/sandbox_common/service_cert.pem 46 | hello world 47 | ``` 48 | 49 | ### Docker 50 | 51 | It is possible to build a runtime image of the JavaScript application via docker: 52 | 53 | ```bash 54 | $ docker build -t ccf-app-template:js-enclave -f docker/ccf_app_js.enclave . 55 | $ docker run --device /dev/sgx_enclave:/dev/sgx_enclave --device /dev/sgx_provision:/dev/sgx_provision -v /dev/sgx:/dev/sgx ccf-app-template:js-enclave 56 | ... 57 | 2022-01-01T12:00:00.000000Z -0.000 0 [info ] ../src/node/node_state.h:1790 | Network TLS connections now accepted 58 | 59 | # Now the CCF service is started and member governance is needed to allow trusted users to interact with the deployed application 60 | ``` 61 | 62 | Or, for the non-SGX (a.k.a. virtual) variant: 63 | 64 | ```bash 65 | $ docker build -t ccf-app-template:js-virtual -f docker/ccf_app_js.virtual . 66 | $ docker run ccf-app-template:js-virtual 67 | ``` 68 | 69 | #### Network governance 70 | 71 | The CCF network is started with one node and one member, you need to execute the following governance steps to initialize the network 72 | 73 | - [Activate the network existing member to start a network governance](https://microsoft.github.io/CCF/main/governance/adding_member.html#activating-a-new-member) 74 | - Build the application and [create a deployment proposal](https://microsoft.github.io/CCF/main/build_apps/js_app_bundle.html#deployment) 75 | - Deploy the application proposal, [using governance calls](https://microsoft.github.io/CCF/main/governance/proposals.html#submitting-a-new-proposal) 76 | - Create and submit [an add users proposal](https://microsoft.github.io/CCF/main/governance/open_network.html#adding-users) 77 | - Open the network for users ([using proposal](https://microsoft.github.io/CCF/main/governance/open_network.html#opening-the-network)) 78 | 79 | ### Bare VM 80 | 81 | The application can be tested using `cchost` on Linux environment. 82 | To start a test CCF network on a Linux environment, it requires [CCF to be intalled](https://microsoft.github.io/CCF/main/build_apps/install_bin.html) or you can create a CCF-enabled VM using [Creating a Virtual Machine in Azure to run CCF](https://github.com/microsoft/CCF/blob/main/getting_started/azure_vm/README.md) 83 | 84 | ```bash 85 | # Start the CCF network using the cchost in 86 | 87 | # Enclave mode 88 | /opt/ccf_sgx/bin/cchost --config ./config/cchost_config_enclave_js.json 89 | 90 | # Or Virtual mode 91 | /opt/ccf_virtual/bin/cchost --config ./config/cchost_config_virtual_js.json 92 | ... 93 | 94 | # Now the CCF network is started and further initialization needed before the interaction with the service 95 | ``` 96 | 97 | The CCF network is started with one node and one member, please follow the [same governance steps as Docker](#network-governance) to initialize the network and check [CCF node config file documentation](https://microsoft.github.io/CCF/main/operations/configuration.html) 98 | 99 | ### Managed CCF 100 | 101 | The application can be tested using [Azure Managed CCF](https://techcommunity.microsoft.com/t5/azure-confidential-computing/microsoft-introduces-preview-of-azure-managed-confidential/ba-p/3648986) `(Pre-release phase)`, you can create Azure Managed CCF service on your subscription, that will give you a ready CCF network 102 | 103 | - First, create the network's initial member certificate, please check [Certificates generation](https://microsoft.github.io/CCF/main/governance/adding_member.html) 104 | - Create a new Azure Managed CCF service (the initial member certificate required as input) 105 | - Build the application and [create a deployment proposal](https://microsoft.github.io/CCF/main/build_apps/js_app_bundle.html#deployment) 106 | - Deploy the application proposal, [using governance calls](https://microsoft.github.io/CCF/main/governance/proposals.html#creating-a-proposal) 107 | - Create and submit [an add users proposal](https://microsoft.github.io/CCF/main/governance/proposals.html#creating-a-proposal) 108 | 109 | ## C++ 110 | 111 | CCF apps can also be written in C++. This offers better performance than JavaScript apps but requires a compilation step and a restart of the CCF node for deployment. 112 | 113 | The C++ sample app is located in the [`cpp/`](cpp/) directory. 114 | 115 | Also check out the [code tour](#code-tour) to get an overview of the C++ app. 116 | 117 | ### Build C++ app 118 | 119 | In the checkout of this repository: 120 | 121 | ```bash 122 | $ cd cpp/ 123 | $ mkdir build && cd build 124 | $ CC="clang-11" CXX="clang++-11" cmake -GNinja .. 125 | $ ninja 126 | $ ls 127 | libccf_app.enclave.so.signed # SGX-enabled application 128 | libccf_app.virtual.so # Virtual application (i.e. insecure!) 129 | ``` 130 | 131 | See [docs](https://microsoft.github.io/CCF/main/build_apps) for complete instructions on how to build a CCF app. 132 | 133 | ### Run C++ app 134 | 135 | ```bash 136 | $ /opt/ccf_virtual/bin/sandbox.sh -p ./libccf_app.virtual.so 137 | Setting up Python environment... 138 | Python environment successfully setup 139 | [12:00:00.000] Virtual mode enabled 140 | [12:00:00.000] Starting 1 CCF node... 141 | [12:00:00.000] Started CCF network with the following nodes: 142 | [12:00:00.000] Node [0] = https://127.0.0.1:8000 143 | [12:00:00.000] You can now issue business transactions to the ./libccf_app.virtual.so application 144 | [12:00:00.000] Keys and certificates have been copied to the common folder: .../ccf-app-template/build/workspace/sandbox_common 145 | [12:00:00.000] See https://microsoft.github.io/CCF/main/use_apps/issue_commands.html for more information 146 | [12:00:00.000] Press Ctrl+C to shutdown the network 147 | ``` 148 | 149 | Or, for an SGX-enabled application (unavailable in development container): `$ /opt/ccf_sgx/bin/sandbox.sh -p ./libccf_app.enclave.so.signed -e release` 150 | 151 | ### Docker 152 | 153 | It is possible to build a runtime image of the C++ application via docker: 154 | 155 | ```bash 156 | $ docker build -t ccf-app-template:cpp-enclave -f docker/ccf_app_cpp.enclave . 157 | $ docker run --device /dev/sgx_enclave:/dev/sgx_enclave --device /dev/sgx_provision:/dev/sgx_provision -v /dev/sgx:/dev/sgx ccf-app-template:cpp-enclave 158 | ... 159 | 2022-01-01T12:00:00.000000Z -0.000 0 [info ] ../src/node/node_state.h:1790 | Network TLS connections now accepted 160 | # It is then possible to interact with the service 161 | ``` 162 | 163 | Or, for the non-SGX (a.k.a. virtual) variant: 164 | 165 | ```bash 166 | $ docker build -t ccf-app-template:cpp-virtual -f docker/ccf_app_cpp.virtual . 167 | $ docker run ccf-app-template:cpp-virtual 168 | ``` 169 | 170 | --- 171 | 172 | ## Dependencies 173 | 174 | If this repository is checked out on a bare VM (e.g. [for SGX deployments](https://docs.microsoft.com/en-us/azure/confidential-computing/quick-create-portal)), the dependencies required to build and run the C++ app can be installed as follows: 175 | 176 | ```bash 177 | $ wget https://github.com/microsoft/CCF/releases/download/ccf-5.0.0/ccf_sgx_5.0.0_amd64.deb 178 | $ sudo dpkg -i ccf_sgx_5.0.0_amd64.deb # Install CCF under /opt/ccf_sgx 179 | $ cat /opt/ccf_sgx/share/VERSION_LONG 180 | ccf-5.0.0 181 | $ /opt/ccf_sgx/getting_started/setup_vm/run.sh /opt/ccf_sgx/getting_started/setup_vm/app-dev.yml # Install dependencies 182 | ``` 183 | 184 | For a non-SGX VM, replace `ccf_sgx` in all the commands above with `ccf_virtual`. 185 | 186 | See the [CCF official docs](https://microsoft.github.io/CCF/main/build_apps/install_bin.html#install-ccf) for more info and [Modern JavaScript application development](https://microsoft.github.io/CCF/main/build_apps/js_app_bundle.html). 187 | 188 | ## Code Tour 189 | 190 | In VSCode, a [code tour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) of the C++ app can be started with: Ctrl + P, `> CodeTour: Start Tour` 191 | --------------------------------------------------------------------------------