├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ └── basic-issue-template.md └── workflows │ ├── cosmwasm-basic.yml │ ├── e2e-tests.yml │ ├── frontend.yml │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── crates ├── cli │ ├── Cargo.toml │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── bin │ │ ├── gen-quote.manifest.template │ │ └── gen-quote.rs │ │ ├── cache.rs │ │ ├── cli.rs │ │ ├── config.rs │ │ ├── handler.rs │ │ ├── handler │ │ ├── contract_build.rs │ │ ├── contract_deploy.rs │ │ ├── dev.rs │ │ ├── enclave_build.rs │ │ ├── enclave_start.rs │ │ ├── handshake.rs │ │ ├── init.rs │ │ ├── print_fmspc.rs │ │ └── utils │ │ │ ├── helpers.rs │ │ │ ├── mod.rs │ │ │ ├── relay.rs │ │ │ └── types.rs │ │ ├── main.rs │ │ ├── request.rs │ │ ├── request │ │ ├── contract_build.rs │ │ ├── contract_deploy.rs │ │ ├── dev.rs │ │ ├── enclave_build.rs │ │ ├── enclave_start.rs │ │ ├── handshake.rs │ │ ├── init.rs │ │ └── print_fmspc.rs │ │ ├── response.rs │ │ └── response │ │ ├── contract_build.rs │ │ ├── contract_deploy.rs │ │ ├── dev.rs │ │ ├── enclave_build.rs │ │ ├── enclave_start.rs │ │ ├── handshake.rs │ │ ├── init.rs │ │ └── print_fmspc.rs ├── common │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── contracts │ ├── README.md │ ├── core │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── SPEC.md │ │ ├── derive │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── lib.rs │ │ └── src │ │ │ ├── error.rs │ │ │ ├── handler.rs │ │ │ ├── handler │ │ │ ├── execute.rs │ │ │ ├── execute │ │ │ │ ├── attested.rs │ │ │ │ ├── sequenced.rs │ │ │ │ ├── session_create.rs │ │ │ │ ├── session_set_pub_key.rs │ │ │ │ └── signed.rs │ │ │ └── instantiate.rs │ │ │ ├── lib.rs │ │ │ ├── msg.rs │ │ │ ├── msg │ │ │ ├── execute.rs │ │ │ ├── execute │ │ │ │ ├── attested.rs │ │ │ │ ├── sequenced.rs │ │ │ │ ├── session_create.rs │ │ │ │ ├── session_set_pub_key.rs │ │ │ │ └── signed.rs │ │ │ ├── instantiate.rs │ │ │ └── query.rs │ │ │ ├── prelude.rs │ │ │ └── state.rs │ ├── dcap-verifier │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── msgs │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ └── src │ │ │ │ └── lib.rs │ │ └── src │ │ │ ├── bin │ │ │ └── dcap_verifier_schema.rs │ │ │ ├── contract.rs │ │ │ └── lib.rs │ ├── tcbinfo │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── data │ │ │ ├── root_ca.pem │ │ │ ├── tcb_signer.pem │ │ │ └── tcbinfo.json │ │ ├── msgs │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ └── src │ │ │ │ └── lib.rs │ │ └── src │ │ │ ├── bin │ │ │ └── tcbinfo_schema.rs │ │ │ ├── contract.rs │ │ │ ├── error.rs │ │ │ ├── helpers.rs │ │ │ ├── integration_tests.rs │ │ │ ├── lib.rs │ │ │ └── state.rs │ └── tee-ra │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── data │ │ ├── DcapRootCACert.pem │ │ ├── fmspc_00906ED50000_2023_07_12.json │ │ ├── hw_quote.dat │ │ ├── leaf_cert.pem │ │ ├── processor_ca.pem │ │ ├── processor_crl.der │ │ ├── qe_identity.json │ │ ├── root_ca.pem │ │ ├── root_crl.der │ │ └── tcb_signer.pem │ │ └── src │ │ ├── intel_sgx.rs │ │ ├── intel_sgx │ │ ├── dcap.rs │ │ └── dcap │ │ │ ├── certificate_chain.rs │ │ │ ├── mc_attest_verifier.rs │ │ │ └── mc_attest_verifier │ │ │ └── dcap.rs │ │ └── lib.rs ├── enclave │ ├── core │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── data │ │ │ ├── qe_identity.json │ │ │ ├── root_ca.pem │ │ │ ├── root_crl.der │ │ │ └── tcb_signer.pem │ │ └── src │ │ │ ├── attestor.rs │ │ │ ├── chain_client.rs │ │ │ ├── chain_client │ │ │ └── default.rs │ │ │ ├── event.rs │ │ │ ├── grpc.rs │ │ │ ├── handler.rs │ │ │ ├── handler │ │ │ ├── instantiate.rs │ │ │ ├── session_create.rs │ │ │ └── session_set_pubkey.rs │ │ │ ├── host.rs │ │ │ ├── key_manager.rs │ │ │ ├── key_manager │ │ │ ├── default.rs │ │ │ └── shared.rs │ │ │ ├── lib.rs │ │ │ ├── proof_of_publication.rs │ │ │ ├── store.rs │ │ │ ├── store │ │ │ └── default.rs │ │ │ └── types.rs │ ├── cw-proof │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── error.rs │ │ │ ├── lib.rs │ │ │ ├── proof │ │ │ ├── cw.rs │ │ │ ├── key.rs │ │ │ ├── mod.rs │ │ │ └── prefix.rs │ │ │ └── verifier │ │ │ ├── cw.rs │ │ │ ├── ics23.rs │ │ │ ├── mod.rs │ │ │ └── multi.rs │ ├── proto │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── build.rs │ │ ├── proto │ │ │ └── quartz.proto │ │ └── src │ │ │ ├── lib.rs │ │ │ └── prost │ │ │ └── quartz.rs │ └── tm-stateless-verifier │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── null_io.rs │ │ └── provider.rs └── utils │ ├── cw-client │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── cli.rs │ │ ├── grpc.rs │ │ └── lib.rs │ ├── cw-prover │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs │ ├── print-fmspc │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs │ └── tm-prover │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── config.rs │ ├── lib.rs │ ├── main.rs │ └── prover.rs ├── docker ├── README.md ├── docker-compose.yml ├── enclave-sgx │ ├── Dockerfile │ └── README.md ├── neutrond │ ├── .dockerignore │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ └── data │ │ ├── accounts │ │ ├── admin.txt │ │ ├── alice.txt │ │ ├── bob.txt │ │ └── charlie.txt │ │ └── entrypoint.sh └── wasmd │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ └── accounts │ ├── admin.txt │ ├── alice.txt │ ├── bob.txt │ └── charlie.txt ├── docs ├── arch │ └── README.md ├── building_apps.md ├── getting_started.md ├── how_it_works.md ├── neutrond_setup.md ├── roadmap.md ├── spec │ └── README.md ├── tcbinfo_and_verifier.md └── tees.md ├── examples ├── pingpong │ ├── .cargo │ │ └── config.toml │ ├── README.md │ ├── cargo-generate.toml │ ├── contracts │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── bin │ │ │ └── schema.rs │ │ └── src │ │ │ ├── contract.rs │ │ │ ├── error.rs │ │ │ ├── lib.rs │ │ │ ├── msg.rs │ │ │ └── state.rs │ ├── enclave │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── build.rs │ │ ├── proto │ │ │ └── pingpong.proto │ │ ├── quartz.manifest.template │ │ └── src │ │ │ ├── bin │ │ │ └── send_message.rs │ │ │ ├── cli.rs │ │ │ ├── event.rs │ │ │ ├── grpc.rs │ │ │ ├── main.rs │ │ │ ├── prost │ │ │ └── pingpong.rs │ │ │ ├── proto.rs │ │ │ └── request.rs │ ├── quartz.neutron_pion-1.toml │ ├── quartz.toml │ └── rust-toolchain.toml └── transfers │ ├── .cargo │ └── config.toml │ ├── README.md │ ├── cargo-generate.toml │ ├── contracts │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── bin │ │ └── schema.rs │ └── src │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ └── state.rs │ ├── enclave │ ├── .DS_Store │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── bin │ │ └── encrypt.rs │ ├── build.rs │ ├── proto │ │ └── transfers.proto │ ├── quartz.manifest.template │ └── src │ │ ├── cli.rs │ │ ├── event.rs │ │ ├── event │ │ ├── query.rs │ │ └── transfer.rs │ │ ├── grpc.rs │ │ ├── main.rs │ │ ├── prost │ │ └── transfers.rs │ │ ├── proto.rs │ │ ├── request.rs │ │ ├── request │ │ ├── query.rs │ │ └── update.rs │ │ └── state.rs │ ├── frontend │ ├── .env.example │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── env.d.ts │ ├── next.config.mjs │ ├── package-lock.json │ ├── package.json │ ├── playwright.config.ts │ ├── postcss.config.mjs │ ├── public │ │ └── favicon.png │ ├── src │ │ ├── app │ │ │ ├── (anon) │ │ │ │ ├── page.tsx │ │ │ │ └── set-seed │ │ │ │ │ └── page.tsx │ │ │ ├── (protected) │ │ │ │ └── dashboard │ │ │ │ │ └── page.tsx │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── not-found.tsx │ │ ├── components │ │ │ ├── DepositModalWindow.tsx │ │ │ ├── EnterSeedModal.tsx │ │ │ ├── Icon │ │ │ │ ├── Icon.tsx │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── LoadingSpinner.tsx │ │ │ ├── ModalWindow │ │ │ │ ├── ModalWindow.tsx │ │ │ │ ├── classNames.ts │ │ │ │ └── index.ts │ │ │ ├── Root │ │ │ │ ├── App.tsx │ │ │ │ ├── GrazWrapper.tsx │ │ │ │ ├── LoadingWrapper.tsx │ │ │ │ ├── Middleware.tsx │ │ │ │ └── index.tsx │ │ │ ├── StyledBox │ │ │ │ ├── StyledBox.tsx │ │ │ │ ├── classNames.ts │ │ │ │ └── index.ts │ │ │ ├── StyledText │ │ │ │ ├── StyledText.tsx │ │ │ │ ├── classNames.tsx │ │ │ │ └── index.ts │ │ │ ├── TransferModalWindow.tsx │ │ │ └── WithdrawModalWindow.tsx │ │ ├── config │ │ │ ├── chain.ts │ │ │ ├── chains │ │ │ │ ├── localNeutron.ts │ │ │ │ ├── localWasm.ts │ │ │ │ └── testnetNeutron.ts │ │ │ └── routes.ts │ │ ├── instrumentation.ts │ │ ├── lib │ │ │ ├── contractMessageBuilders.ts │ │ │ ├── ephemeralKeypair.ts │ │ │ ├── isValidAddress.ts │ │ │ ├── notifications.tsx │ │ │ ├── tw.ts │ │ │ └── wasmEventHandler.ts │ │ └── state │ │ │ └── useGlobalState.ts │ ├── tailwind.config.ts │ ├── tests │ │ ├── e2e │ │ │ ├── auth.spec.ts │ │ │ ├── extensions │ │ │ │ └── .gitkeep │ │ │ ├── fixtures.ts │ │ │ ├── helpers │ │ │ │ ├── connectWalet.ts │ │ │ │ ├── getBalance.ts │ │ │ │ ├── importWallet.ts │ │ │ │ ├── setSeedPhrase.ts │ │ │ │ ├── signTx.ts │ │ │ │ └── swapWallet.ts │ │ │ ├── seed-phrase.spec.ts │ │ │ └── transfers.spec.ts │ │ └── global.setup.ts │ └── tsconfig.json │ ├── quartz.neutron_pion-1.toml │ ├── quartz.toml │ ├── rust-toolchain.toml │ └── sgx_default_qcnl.conf ├── release.sh └── rust-toolchain.toml /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !crates 3 | !examples 4 | !Cargo.lock 5 | !Cargo.toml 6 | **/.cache 7 | **/frontend 8 | **/target 9 | **/.gitignore 10 | **/Makefile 11 | **/*.md 12 | !**/README.md 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/basic-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic issue template 3 | about: Create an issue with a summary and acceptance criteria 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Summary 11 | 12 | 13 | 14 | ## Acceptance Criteria 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/cosmwasm-basic.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml 2 | 3 | name: Cosmwasm basic 4 | 5 | on: 6 | pull_request: 7 | paths: 8 | - .github/workflows/cosmwasm-basic.yml 9 | - crates/** 10 | - examples/** 11 | - "!examples/transfers/frontend/**" 12 | push: 13 | branches: main 14 | paths: 15 | - .github/workflows/cosmwasm-basic.yml 16 | - crates/** 17 | - examples/** 18 | - "!examples/transfers/frontend/**" 19 | 20 | env: 21 | CARGO_INCREMENTAL: 0 22 | CARGO_PROFILE_DEV_DEBUG: 1 23 | CARGO_PROFILE_RELEASE_DEBUG: 1 24 | RUST_BACKTRACE: short 25 | CARGO_NET_RETRY: 10 26 | RUSTUP_MAX_RETRIES: 10 27 | CARGO_TERM_COLOR: always 28 | 29 | defaults: 30 | run: 31 | working-directory: examples/transfers/contracts 32 | 33 | jobs: 34 | test-wasm: 35 | name: Test Suite 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@v2 40 | 41 | - name: Install protobuf-compiler 42 | run: sudo apt-get install -y protobuf-compiler 43 | 44 | - name: Install wasm32-unknown-unknown toolchain 45 | uses: dtolnay/rust-toolchain@stable 46 | with: 47 | target: wasm32-unknown-unknown 48 | 49 | - name: Run unit tests 50 | run: cargo unit-test --locked 51 | env: 52 | RUST_BACKTRACE: 1 53 | 54 | - name: Compile WASM contract 55 | run: cargo wasm --locked 56 | env: 57 | RUSTFLAGS: "-C link-arg=-s" 58 | 59 | schema: 60 | name: Schema 61 | runs-on: ubuntu-latest 62 | steps: 63 | - name: Checkout sources 64 | uses: actions/checkout@v2 65 | 66 | - name: Install stable toolchain 67 | uses: dtolnay/rust-toolchain@stable 68 | with: 69 | target: wasm32-unknown-unknown 70 | 71 | - name: Generate Schema 72 | run: cargo schema --locked 73 | 74 | - name: Schema Changes 75 | # fails if any changes not committed 76 | run: git diff --exit-code schema 77 | -------------------------------------------------------------------------------- /.github/workflows/e2e-tests.yml: -------------------------------------------------------------------------------- 1 | name: E2E tests 2 | on: 3 | pull_request: 4 | types: [synchronize, ready_for_review] 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | if: ${{ !github.event.pull_request.draft }} 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Setup Node 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version: "20.x" 17 | check-latest: true 18 | 19 | - name: Run Backend stack 20 | run: docker compose -f docker/docker-compose.yml up -d --build 21 | 22 | - name: Wait for enclave to listen 23 | timeout-minutes: 25 24 | run: | 25 | echo "Waiting for handshake completion..." 26 | while ! docker logs enclave 2>&1 | grep -q "Enclave is listening"; do 27 | sleep 30 28 | done 29 | 30 | - name: Capture logs from CLI 31 | working-directory: examples/transfers/frontend 32 | run: docker logs --tail 50 enclave &> enclave.logs.txt 33 | 34 | - name: Setup Frontend environment variables for quartz 35 | working-directory: examples/transfers/frontend 36 | run: | 37 | cp .env.example .env.local 38 | sed -i "s/^NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=.*/NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=$(grep 'Contract Address:' enclave.logs.txt | awk '{print $NF}')/" .env.local 39 | sed -i "s/^NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=.*/NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=$(grep 'Handshake complete:' enclave.logs.txt | awk '{print $NF}')/" .env.local 40 | 41 | - name: Install Frontend dependencies 42 | working-directory: examples/transfers/frontend 43 | run: npm ci 44 | 45 | - name: Run all E2E tests 46 | working-directory: examples/transfers/frontend 47 | run: xvfb-run --auto-servernum --server-num=1 --server-args='-screen 0, 1920x1080x24' npm run test 48 | -------------------------------------------------------------------------------- /.github/workflows/frontend.yml: -------------------------------------------------------------------------------- 1 | name: Frontend 2 | on: 3 | pull_request: 4 | paths: 5 | - .github/workflows/frontend.yml 6 | - examples/transfers/frontend/** 7 | defaults: 8 | run: 9 | working-directory: examples/transfers/frontend 10 | jobs: 11 | linting: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: "20.x" 18 | check-latest: true 19 | - run: npm ci 20 | - run: npm run lint 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | *.manifest 4 | *.manifest.sgx 5 | *.sig 6 | *.hash 7 | *.height 8 | *light-client-proof.json 9 | *output 10 | target/ 11 | artifacts/ 12 | .secrets/ 13 | **/.env.local 14 | 15 | #cli 16 | crates/cli/quartz.toml 17 | .cache/ 18 | 19 | # Build results 20 | /artifacts 21 | target/ 22 | schema/ 23 | 24 | # Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) 25 | .cargo-ok 26 | 27 | # Text file backups 28 | **/*.rs.bk 29 | 30 | # IDEs 31 | *.iml 32 | .idea 33 | .vscode 34 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | reorder_modules = true 3 | 4 | # Automatically fix deprecated style. 5 | use_field_init_shorthand = true 6 | use_try_shorthand = true 7 | 8 | # unstable features 9 | group_imports = "StdExternalCrate" 10 | imports_granularity = "Crate" 11 | format_code_in_doc_comments = true 12 | -------------------------------------------------------------------------------- /crates/cli/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.81 AS build 2 | 3 | ENV TARGET=wasm32-unknown-unknown 4 | ARG CARGO_FLAGS="" 5 | 6 | COPY . /opt 7 | 8 | # Temporarily disable GPG checks 9 | RUN echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid-until \ 10 | && echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure \ 11 | && echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure 12 | 13 | RUN apt-get update && \ 14 | apt-get install -y --no-install-recommends \ 15 | protobuf-compiler \ 16 | openssl \ 17 | clang \ 18 | ca-certificates \ 19 | wget 20 | 21 | # Install Go (multi-arch) 22 | RUN ARCH=$(uname -m) && \ 23 | case ${ARCH} in \ 24 | x86_64) GO_ARCH="amd64" ;; \ 25 | aarch64|arm64) GO_ARCH="arm64" ;; \ 26 | *) echo "Unsupported architecture: ${ARCH}" && exit 1 ;; \ 27 | esac && \ 28 | wget https://go.dev/dl/go1.22.0.linux-${GO_ARCH}.tar.gz && \ 29 | tar -C /usr/local -xzf go1.22.0.linux-${GO_ARCH}.tar.gz && \ 30 | rm go1.22.0.linux-${GO_ARCH}.tar.gz 31 | 32 | # Install Neutrond (original amd64 version) 33 | RUN wget https://github.com/neutron-org/neutron/releases/download/v4.2.4/neutrond-linux-amd64 && \ 34 | chmod +x neutrond-linux-amd64 && \ 35 | cp neutrond-linux-amd64 /usr/local/bin/neutrond 36 | 37 | ENV PATH="/usr/local/go/bin:${PATH}" 38 | 39 | RUN go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest && \ 40 | cargo install websocat 41 | 42 | RUN rustup target add "$TARGET" 43 | WORKDIR /opt/crates/cli 44 | RUN cargo build --locked --release ${CARGO_FLAGS} 45 | RUN cp /opt/target/release/quartz /usr/local/bin/ 46 | 47 | # Clean up 48 | RUN apt-get remove -y wget && \ 49 | apt-get clean && \ 50 | rm -rf /var/lib/apt/lists/* 51 | 52 | # Create a directory for the app 53 | RUN mkdir -p /opt/examples/transfers 54 | 55 | ENTRYPOINT ["quartz", "--mock-sgx", "--app-dir", "/opt/examples/transfers", "dev", "--unsafe-trust-latest", "--contract-manifest", "/opt/examples/transfers/contracts/Cargo.toml", "--init-msg", "{\"denom\":\"untrn\"}"] -------------------------------------------------------------------------------- /crates/cli/README.md: -------------------------------------------------------------------------------- 1 | # quartz CLI 2 | 3 | A CLI tool to manage Quartz applications. The `quartz` CLI tool is designed to streamline the development and deployment 4 | process of Quartz applications. 5 | 6 | It provides helpful information about each command and its options. To get a list of all available subcommands and their 7 | descriptions, use the `--help` flag: 8 | 9 | ```shell 10 | $ quartz --help 11 | 12 | Quartz 0.1.0 13 | A CLI tool to manage Quartz applications 14 | 15 | USAGE: 16 | quartz [SUBCOMMAND] 17 | 18 | OPTIONS: 19 | -h, --help Print help information 20 | -V, --version Print version information 21 | 22 | SUBCOMMANDS: 23 | init Create base Quartz app directory from template 24 | build Build the contract and enclave binaries 25 | enclave Enclave subcommads to configure Gramine, build, sign, and start the enclave binary 26 | contract Contract subcommads to build, deploy the WASM binary to the blockchain and call instantiate 27 | handshake Run the handshake between the contract and enclave 28 | ``` 29 | 30 | ## Installation 31 | 32 | To install Quartz, ensure you have Rust and Cargo installed. Then run: 33 | 34 | ```shell 35 | cargo install quartz-rs 36 | ``` 37 | 38 | ## Usage 39 | 40 | See the [getting started](/docs/getting_started.md). 41 | 42 | Run `quartz init` to copy the example app into a new directory. Quartz apps are 43 | organized like: 44 | 45 | ```shell 46 | myapp/ 47 | ├── contracts/ 48 | ├── enclave/ 49 | ├── frontend/ 50 | └── README.md 51 | ``` 52 | -------------------------------------------------------------------------------- /crates/cli/src/bin/gen-quote.manifest.template: -------------------------------------------------------------------------------- 1 | # Manifest file for creating dummy quotes 2 | 3 | libos.entrypoint = "{{ gen_quote_bin_path }}" 4 | 5 | loader.log_level = "{{ log_level }}" 6 | loader.entrypoint.uri = "file:{{ gramine.libos }}" 7 | loader.env.LD_LIBRARY_PATH = "/lib:/usr/local/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}" 8 | loader.env.HOME = "{{ home }}" 9 | loader.env.INSIDE_SGX = "1" 10 | loader.env.TLS = { passthrough = true } 11 | loader.env.RA_TYPE = { passthrough = true } 12 | loader.env.RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE = { passthrough = true } 13 | loader.env.RA_TLS_ALLOW_OUTDATED_TCB_INSECURE = { passthrough = true } 14 | loader.env.RA_TLS_MRENCLAVE = { passthrough = true } 15 | loader.env.RA_TLS_MRSIGNER = { passthrough = true } 16 | loader.env.RA_TLS_ISV_SVN = { passthrough = true } 17 | loader.env.RA_TLS_ISV_PROD_ID = { passthrough = true } 18 | loader.env.MYAPP_DATA = { passthrough = true } 19 | 20 | fs.mounts = [ 21 | { uri = "file:{{ gramine.runtimedir() }}", path = "/lib" }, 22 | { uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" }, 23 | { uri = "file:/usr/{{ arch_libdir }}", path = "/usr{{ arch_libdir }}" }, 24 | { uri = "file:{{ gen_quote_bin_path }}", path = "{{ gen_quote_bin_path }}" }, 25 | ] 26 | 27 | sgx.enclave_size = "512M" 28 | sgx.max_threads = 4 29 | sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} 30 | 31 | sgx.remote_attestation = "{{ ra_type }}" 32 | 33 | sgx.trusted_files = [ 34 | "file:{{ gramine.libos }}", 35 | "file:{{ gen_quote_bin_path }}", 36 | "file:{{ gramine.runtimedir() }}/", 37 | "file:{{ arch_libdir }}/", 38 | "file:/usr/{{ arch_libdir }}/", 39 | ] 40 | 41 | sys.enable_sigterm_injection = true 42 | -------------------------------------------------------------------------------- /crates/cli/src/bin/gen-quote.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{self, Read, Write}, 4 | }; 5 | 6 | fn main() -> io::Result<()> { 7 | let user_data = [0u8; 64]; 8 | let mut user_report_data = File::create("/dev/attestation/user_report_data")?; 9 | user_report_data.write_all(user_data.as_slice())?; 10 | user_report_data.flush()?; 11 | 12 | let mut file = File::open("/dev/attestation/quote")?; 13 | let mut buffer = Vec::new(); 14 | file.read_to_end(&mut buffer)?; 15 | 16 | let quote_hex = hex::encode(&buffer); 17 | print!("{}", quote_hex); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /crates/cli/src/handler.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use color_eyre::{Report, Result}; 3 | 4 | use crate::{config::Config, request::Request, response::Response}; 5 | 6 | pub mod utils; 7 | // commands 8 | pub mod contract_build; 9 | pub mod contract_deploy; 10 | pub mod dev; 11 | pub mod enclave_build; 12 | pub mod enclave_start; 13 | pub mod handshake; 14 | pub mod init; 15 | pub mod print_fmspc; 16 | 17 | #[async_trait] 18 | pub trait Handler { 19 | type Response; 20 | 21 | async fn handle + Send>(self, config: C) -> Result; 22 | } 23 | 24 | #[async_trait] 25 | impl Handler for Request { 26 | type Response = Response; 27 | 28 | async fn handle + Send>(self, config: C) -> Result { 29 | match self { 30 | Request::Init(request) => request.handle(config).await, 31 | Request::Handshake(request) => request.handle(config).await, 32 | Request::ContractBuild(request) => request.handle(config).await, 33 | Request::ContractDeploy(request) => request.handle(config).await, 34 | Request::EnclaveBuild(request) => request.handle(config).await, 35 | Request::EnclaveStart(request) => request.handle(config).await, 36 | Request::Dev(request) => request.handle(config).await, 37 | Request::PrintFmspc(request) => request.handle(config).await, 38 | } 39 | .map(Into::into) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/cli/src/handler/contract_build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use async_trait::async_trait; 4 | use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Report, Result}; 5 | use tracing::{debug, info}; 6 | 7 | use crate::{ 8 | config::Config, 9 | handler::Handler, 10 | request::contract_build::ContractBuildRequest, 11 | response::{contract_build::ContractBuildResponse, Response}, 12 | }; 13 | 14 | #[async_trait] 15 | impl Handler for ContractBuildRequest { 16 | type Response = Response; 17 | 18 | async fn handle + Send>(self, config: C) -> Result { 19 | let config = config.as_ref(); 20 | info!("{}", "\nPeforming Contract Build".blue().bold()); 21 | 22 | let mut cargo = Command::new("cargo"); 23 | let command = cargo 24 | .arg("build") 25 | .arg("--release") 26 | .args(["--target", "wasm32-unknown-unknown"]) 27 | .arg("--lib") 28 | .args([ 29 | "--target-dir", 30 | &config.app_dir.join("target").display().to_string(), 31 | ]) 32 | .args([ 33 | "--manifest-path", 34 | &self.contract_manifest.display().to_string(), 35 | ]) 36 | .env("RUSTFLAGS", "-C link-arg=-s"); 37 | 38 | if config.mock_sgx { 39 | debug!("Building with mock-sgx enabled"); 40 | command.arg("--features=mock-sgx"); 41 | } 42 | 43 | info!("{}", "🚧 Building contract binary ...".green().bold()); 44 | let status = command.status()?; 45 | 46 | if !status.success() { 47 | return Err(eyre!("Couldn't build contract. \n{:?}", status)); 48 | } 49 | 50 | config.log_build(false).await?; 51 | 52 | Ok(ContractBuildResponse.into()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/cli/src/handler/enclave_build.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Report, Result}; 3 | use tokio::process::Command; 4 | use tracing::{debug, info}; 5 | 6 | use crate::{ 7 | config::Config, 8 | handler::Handler, 9 | request::enclave_build::EnclaveBuildRequest, 10 | response::{enclave_build::EnclaveBuildResponse, Response}, 11 | }; 12 | 13 | #[async_trait] 14 | impl Handler for EnclaveBuildRequest { 15 | type Response = Response; 16 | 17 | async fn handle + Send>(self, config: C) -> Result { 18 | let config = config.as_ref(); 19 | info!("{}", "\nPeforming Enclave Build".blue().bold()); 20 | 21 | let enclave_dir = config.app_dir.join("enclave"); 22 | 23 | let mut cargo = Command::new("cargo"); 24 | let command = cargo 25 | .arg("build") 26 | .args(["--target-dir", &config.app_dir.join("target").display().to_string()]) // TODO: Where should this be set to? 27 | .args(["--manifest-path", &enclave_dir.join("Cargo.toml").display().to_string(), 28 | ]); 29 | 30 | if config.mock_sgx { 31 | debug!("Building with mock-sgx enabled"); 32 | command.arg("--features=mock-sgx"); 33 | } 34 | 35 | if config.release { 36 | debug!("Targetting release"); 37 | command.arg("--release"); 38 | } 39 | 40 | info!("{}", "🚧 Running build command ...".green().bold()); 41 | let status = command.status().await?; 42 | 43 | if !status.success() { 44 | return Err(eyre!("Couldn't build enclave. {:?}", status)); 45 | } 46 | 47 | config.log_build(true).await?; 48 | 49 | Ok(EnclaveBuildResponse.into()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/cli/src/handler/init.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use cargo_generate::{generate, GenerateArgs, TemplatePath, Vcs}; 3 | use color_eyre::{eyre::Context, owo_colors::OwoColorize, Report, Result}; 4 | use tokio::fs; 5 | use tracing::info; 6 | 7 | use crate::{ 8 | config::Config, 9 | handler::Handler, 10 | request::init::InitRequest, 11 | response::{init::InitResponse, Response}, 12 | }; 13 | 14 | #[async_trait] 15 | impl Handler for InitRequest { 16 | type Response = Response; 17 | 18 | // TODO: Add non-template init method 19 | async fn handle + Send>(self, config: C) -> Result { 20 | let config = config.as_ref(); 21 | info!("{}", "\nPeforming Init".blue().bold()); 22 | 23 | let parent = self 24 | .name 25 | .parent() 26 | .map(|p| p.to_path_buf()) 27 | .expect("path already validated"); 28 | fs::create_dir_all(&parent) 29 | .await 30 | .wrap_err("Error creating directories to target app folder")?; 31 | 32 | let file_name = self 33 | .name 34 | .file_name() 35 | .and_then(|f| f.to_str()) 36 | .expect("path already validated"); 37 | 38 | let wasm_pack_args = GenerateArgs { 39 | name: Some(file_name.to_string()), 40 | destination: Some(config.app_dir.join(parent)), 41 | overwrite: true, 42 | vcs: Some(Vcs::Git), 43 | template_path: TemplatePath { 44 | git: Some("https://github.com/informalsystems/cycles-quartz.git".to_string()), 45 | ..TemplatePath::default() 46 | }, 47 | ..GenerateArgs::default() 48 | }; 49 | 50 | let result_dir = generate(wasm_pack_args) 51 | .expect("something went wrong!") 52 | .display() 53 | .to_string(); 54 | 55 | info!("\n{}", "It's TEE time.".green().bold()); 56 | Ok(InitResponse { result_dir }.into()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/cli/src/handler/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod helpers; 2 | pub mod relay; 3 | pub mod types; 4 | -------------------------------------------------------------------------------- /crates/cli/src/handler/utils/types.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use tendermint::{abci::Event as TmEvent, Hash}; 3 | 4 | // Rust libraries don't seem to implement this type from the wasmd go implementation 5 | // TODO: Replace String with types from Rust libraries 6 | // TODO: Move this into WasmdClient 7 | #[derive(Deserialize, Debug, Default)] 8 | pub struct WasmdTxResponse { 9 | pub height: String, 10 | pub txhash: Hash, 11 | pub codespace: String, 12 | pub code: u32, 13 | pub data: String, 14 | pub raw_log: String, 15 | pub logs: Vec, 16 | pub info: String, 17 | pub gas_wanted: String, 18 | pub gas_used: String, 19 | pub tx: Option, 20 | pub timestamp: String, 21 | pub events: Vec, 22 | } 23 | 24 | #[derive(Serialize, Deserialize, Debug)] 25 | pub struct Attribute { 26 | pub key: String, 27 | pub value: String, 28 | } 29 | 30 | #[derive(Serialize, Deserialize, Debug)] 31 | pub struct Event { 32 | pub attributes: Vec, 33 | pub r#type: String, 34 | } 35 | 36 | #[derive(Serialize, Deserialize, Debug)] 37 | pub struct Log { 38 | pub events: Vec, 39 | pub msg_index: u32, 40 | } 41 | -------------------------------------------------------------------------------- /crates/cli/src/request/contract_build.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use crate::request::Request; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct ContractBuildRequest { 7 | pub contract_manifest: PathBuf, 8 | } 9 | 10 | impl From for Request { 11 | fn from(request: ContractBuildRequest) -> Self { 12 | Self::ContractBuild(request) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/cli/src/request/contract_deploy.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::PathBuf}; 2 | 3 | use color_eyre::{eyre::Context, Result}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::request::Request; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct ContractDeployRequest { 10 | pub init_msg: serde_json::Value, 11 | pub label: String, 12 | pub contract_manifest: PathBuf, 13 | } 14 | 15 | impl From for Request { 16 | fn from(request: ContractDeployRequest) -> Self { 17 | Self::ContractDeploy(request) 18 | } 19 | } 20 | 21 | impl ContractDeployRequest { 22 | pub fn checked_init(init_msg: String) -> Result { 23 | let parsed: GenericQuartzInit = serde_json::from_str(&init_msg) 24 | .wrap_err("Init message doesn't contain mandatory quartz field")?; 25 | 26 | Ok(parsed) 27 | } 28 | } 29 | 30 | #[derive(Serialize, Deserialize, Debug, Clone)] 31 | pub struct GenericQuartzInit { 32 | pub quartz: serde_json::Value, 33 | #[serde(flatten)] 34 | extra: HashMap, 35 | } 36 | -------------------------------------------------------------------------------- /crates/cli/src/request/dev.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use cosmrs::AccountId; 4 | use quartz_common::enclave::types::Fmspc; 5 | 6 | use crate::request::Request; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct DevRequest { 10 | pub watch: bool, 11 | pub unsafe_trust_latest: bool, 12 | pub init_msg: serde_json::Value, 13 | pub label: String, 14 | pub contract_manifest: PathBuf, 15 | pub release: bool, 16 | pub fmspc: Option, 17 | pub tcbinfo_contract: Option, 18 | pub dcap_verifier_contract: Option, 19 | } 20 | 21 | impl From for Request { 22 | fn from(request: DevRequest) -> Self { 23 | Self::Dev(request) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/cli/src/request/enclave_build.rs: -------------------------------------------------------------------------------- 1 | use crate::request::Request; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct EnclaveBuildRequest {} 5 | 6 | impl From for Request { 7 | fn from(request: EnclaveBuildRequest) -> Self { 8 | Self::EnclaveBuild(request) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/cli/src/request/enclave_start.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::Result; 2 | use cosmrs::AccountId; 3 | use quartz_common::enclave::types::Fmspc; 4 | use reqwest::Url; 5 | use tendermint::{block::Height, Hash}; 6 | use tracing::debug; 7 | 8 | use crate::{config::Config, handler::utils::helpers::query_latest_height_hash, request::Request}; 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct EnclaveStartRequest { 12 | pub unsafe_trust_latest: bool, 13 | pub fmspc: Option, 14 | pub pccs_url: Option, 15 | pub tcbinfo_contract: Option, 16 | pub dcap_verifier_contract: Option, 17 | } 18 | 19 | impl From for Request { 20 | fn from(request: EnclaveStartRequest) -> Self { 21 | Self::EnclaveStart(request) 22 | } 23 | } 24 | 25 | impl EnclaveStartRequest { 26 | /// Returns the trusted hash and height 27 | pub fn get_hash_height(&self, config: &Config) -> Result<(Height, Hash)> { 28 | if self.unsafe_trust_latest || config.trusted_height == 0 || config.trusted_hash.is_empty() 29 | { 30 | debug!("querying latest trusted hash & height from node"); 31 | let (trusted_height, trusted_hash) = query_latest_height_hash(config.node_url.clone())?; 32 | 33 | Ok((trusted_height, trusted_hash)) 34 | } else { 35 | debug!("reusing config trusted hash & height"); 36 | Ok(( 37 | config.trusted_height.try_into()?, 38 | config.trusted_hash.parse()?, 39 | )) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/cli/src/request/handshake.rs: -------------------------------------------------------------------------------- 1 | use cosmrs::AccountId; 2 | 3 | use crate::request::Request; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct HandshakeRequest { 7 | pub contract: AccountId, 8 | pub unsafe_trust_latest: bool, 9 | } 10 | 11 | impl From for Request { 12 | fn from(request: HandshakeRequest) -> Self { 13 | Self::Handshake(request) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/cli/src/request/init.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use color_eyre::{eyre::eyre, Report, Result}; 4 | 5 | use crate::request::Request; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct InitRequest { 9 | pub name: PathBuf, 10 | } 11 | 12 | impl TryFrom for Request { 13 | type Error = Report; 14 | 15 | fn try_from(request: InitRequest) -> Result { 16 | if request.name.extension().is_some() { 17 | return Err(eyre!("Path is not a directory: {}", request.name.display())); 18 | } else if request.name.exists() { 19 | return Err(eyre!( 20 | "Directory already exists: {}", 21 | request.name.display() 22 | )); 23 | } 24 | 25 | Ok(Request::Init(request)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/cli/src/request/print_fmspc.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Url; 2 | 3 | use crate::request::Request; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct PrintFmspcRequest { 7 | pub pccs_url: Option, 8 | } 9 | 10 | impl From for Request { 11 | fn from(request: PrintFmspcRequest) -> Self { 12 | Self::PrintFmspc(request) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/cli/src/response.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::{ 4 | contract_build::ContractBuildResponse, contract_deploy::ContractDeployResponse, 5 | dev::DevResponse, enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse, 6 | handshake::HandshakeResponse, init::InitResponse, print_fmspc::PrintFmspcResponse, 7 | }; 8 | 9 | pub mod contract_build; 10 | pub mod contract_deploy; 11 | pub mod dev; 12 | pub mod enclave_build; 13 | pub mod enclave_start; 14 | pub mod handshake; 15 | pub mod init; 16 | 17 | pub mod print_fmspc; 18 | 19 | #[derive(Clone, Debug, Serialize)] 20 | pub enum Response { 21 | Init(InitResponse), 22 | Handshake(HandshakeResponse), 23 | ContractBuild(ContractBuildResponse), 24 | ContractDeploy(ContractDeployResponse), 25 | EnclaveBuild(EnclaveBuildResponse), 26 | EnclaveStart(EnclaveStartResponse), 27 | Dev(DevResponse), 28 | PrintFmspc(PrintFmspcResponse), 29 | } 30 | -------------------------------------------------------------------------------- /crates/cli/src/response/contract_build.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct ContractBuildResponse; 7 | 8 | impl From for Response { 9 | fn from(response: ContractBuildResponse) -> Self { 10 | Self::ContractBuild(response) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /crates/cli/src/response/contract_deploy.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct ContractDeployResponse { 7 | pub code_id: u64, 8 | pub contract_addr: String, 9 | } 10 | 11 | impl From for Response { 12 | fn from(response: ContractDeployResponse) -> Self { 13 | Self::ContractDeploy(response) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/cli/src/response/dev.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct DevResponse; 7 | 8 | impl From for Response { 9 | fn from(response: DevResponse) -> Self { 10 | Self::Dev(response) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /crates/cli/src/response/enclave_build.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct EnclaveBuildResponse; 7 | 8 | impl From for Response { 9 | fn from(response: EnclaveBuildResponse) -> Self { 10 | Self::EnclaveBuild(response) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /crates/cli/src/response/enclave_start.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct EnclaveStartResponse; 7 | 8 | impl From for Response { 9 | fn from(response: EnclaveStartResponse) -> Self { 10 | Self::EnclaveStart(response) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /crates/cli/src/response/handshake.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize, Default)] 6 | pub struct HandshakeResponse { 7 | pub pub_key: String, 8 | } 9 | 10 | impl From for Response { 11 | fn from(response: HandshakeResponse) -> Self { 12 | Self::Handshake(response) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/cli/src/response/init.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct InitResponse { 7 | pub result_dir: String, 8 | } 9 | 10 | impl From for Response { 11 | fn from(response: InitResponse) -> Self { 12 | Self::Init(response) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/cli/src/response/print_fmspc.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::response::Response; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct PrintFmspcResponse { 7 | pub fmspc: String, 8 | } 9 | 10 | impl From for Response { 11 | fn from(response: PrintFmspcResponse) -> Self { 12 | Self::PrintFmspc(response) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-common" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | A wrapper crate around quartz-contract-core, quartz-enclave-core, and quartz-proto. 15 | """ 16 | 17 | [features] 18 | full = ["contract", "enclave", "proto"] 19 | contract = ["dep:quartz-contract-core"] 20 | enclave = ["dep:quartz-enclave-core", "proto"] 21 | proto = ["dep:quartz-proto"] 22 | mock-sgx-cw = ["quartz-contract-core/mock-sgx"] 23 | mock-sgx-enclave = ["quartz-enclave-core/mock-sgx"] 24 | 25 | [dependencies] 26 | quartz-contract-core = { workspace = true, optional = true } 27 | quartz-proto = { workspace = true, optional = true } 28 | quartz-enclave-core = { workspace = true, optional = true } 29 | -------------------------------------------------------------------------------- /crates/common/README.md: -------------------------------------------------------------------------------- 1 | # common 2 | 3 | A simple crate to re-export quartz core and protobufs: 4 | 5 | ```rust 6 | #[cfg(feature = "contract")] 7 | pub use quartz_contract_core as contract; 8 | #[cfg(feature = "enclave")] 9 | pub use quartz_enclave_core as enclave; 10 | #[cfg(feature = "proto")] 11 | pub use quartz_proto::quartz as proto; 12 | ``` 13 | -------------------------------------------------------------------------------- /crates/common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "contract")] 2 | pub use quartz_contract_core as contract; 3 | #[cfg(feature = "enclave")] 4 | pub use quartz_enclave_core as enclave; 5 | #[cfg(feature = "proto")] 6 | pub use quartz_proto::quartz as proto; 7 | -------------------------------------------------------------------------------- /crates/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Quartz CosmWasm Packages 2 | 3 | CosmWasm packages for building Quartz apps and verifying remote 4 | attestations from SGX. 5 | 6 | The main interface for CosmWasm developers is package `core`. 7 | 8 | ## Packages 9 | 10 | 1. `quartz-contract-core`. High-level framework for building attestation-aware smart contracts by wrapping CosmWasm messages in TEE attestations (e.g. DCAP). 11 | 1. `quartz-dcap-verifier`: Standalone smart contract for verifying DCAP attestations that can be called by other contracts. 12 | 1. `quartz-tee-ra`: Implements core types for SGX quotes and verification for 13 | DCAP attestations 14 | 1. `quartz-tcbinfo`: Standalone smart contract for verifying attestations come 15 | from valid/secure Trusted Compute Base (TCB) 16 | -------------------------------------------------------------------------------- /crates/contracts/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-contract-core" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | A CosmWasm framework for securely interacting with Intel SGX enclaves. 15 | """ 16 | 17 | [features] 18 | default = [] 19 | mock-sgx = [] 20 | std = ["k256/std", "serde/std", "serde_json/std", "sha2/std", "cosmwasm-std/std"] 21 | library = [] 22 | 23 | [dependencies] 24 | # external 25 | ciborium.workspace = true 26 | hex.workspace = true 27 | k256.workspace = true 28 | serde.workspace = true 29 | serde_json.workspace = true 30 | sha2.workspace = true 31 | thiserror.workspace = true 32 | 33 | # cosmos 34 | cw-storage-plus.workspace = true 35 | cosmwasm-schema.workspace = true 36 | cosmwasm-std.workspace = true 37 | 38 | # quartz 39 | quartz-dcap-verifier-msgs.workspace = true 40 | quartz-tee-ra.workspace = true 41 | quartz-tcbinfo-msgs.workspace = true 42 | 43 | [dev-dependencies] 44 | serde_json.workspace = true 45 | -------------------------------------------------------------------------------- /crates/contracts/core/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Quartz CosmWasm (quartz-contract-core) 3 | 4 | Quartz CosmWasm (quartz-contract-core) is a high-level framework for building attestation-aware smart contracts on CosmWasm. It provides a robust foundation for developing secure, Intel SGX-based contracts with built-in remote attestation support. 5 | 6 | ## Features 7 | 8 | - `Attested` wrapper for a message and its attestation 9 | - MockAttestation type for development ease 10 | - Session management for secure communication between contract and enclave 11 | - Verify DCAP attestations (by calling `dcap-verifier` and `tcbinfo` contracts) 12 | - Mock SGX support for testing environments 13 | 14 | See also the [spec.md](./SPEC.md) 15 | 16 | ## Installation 17 | 18 | Add `quartz-contract-core` to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | quartz-contract-core = { path = "../packages/quartz-contract-core" } 23 | ``` 24 | 25 | ## Usage 26 | 27 | Here's a basic example of how to use `quartz-contract-core` in your CosmWasm contract: 28 | 29 | ```rust 30 | use quartz_cw::prelude::*; 31 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; 32 | 33 | pub fn execute( 34 | deps: DepsMut, 35 | env: Env, 36 | info: MessageInfo, 37 | msg: QuartzExecuteMsg, 38 | ) -> Result { 39 | match msg { 40 | QuartzExecuteMsg::Attested(attested_msg) => { 41 | // Handle attested message 42 | // Verification of the attestation is done automatically 43 | let result = attested_msg.handle(deps, env, info)?; 44 | Ok(result) 45 | }, 46 | // Other message handlers... 47 | } 48 | } 49 | ``` 50 | 51 | ## Key Components 52 | 53 | 1. `Attested`: A wrapper struct for holding a message and its attestation. 54 | 2. `Attestation`: A trait for attestation types (DCAP, Mock). 55 | 3. `HasUserData`: A trait for extracting user data from attestations. 56 | 4. `RawHandler`: A trait for handling raw messages. 57 | 58 | ## Configuration 59 | 60 | You can enable mock SGX support for testing by adding the `mock-sgx` feature to your `Cargo.toml`: 61 | 62 | ```toml 63 | [dependencies] 64 | quartz-contract-core = { path = "../packages/quartz-contract-core", features = ["mock-sgx"] } 65 | ``` 66 | 67 | ## Testing 68 | 69 | To run the tests: 70 | 71 | ```sh 72 | cargo test 73 | ``` 74 | -------------------------------------------------------------------------------- /crates/contracts/core/derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-contract-core-derive" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | description = """ 13 | Derive macros for Quartz contracts. 14 | """ 15 | 16 | [dependencies] 17 | quote = "1.0" 18 | syn = { version = "1.0", features = ["full"] } 19 | 20 | [lib] 21 | proc-macro = true 22 | -------------------------------------------------------------------------------- /crates/contracts/core/derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(UserData)] 6 | pub fn user_data_derive(input: TokenStream) -> TokenStream { 7 | let input = parse_macro_input!(input as DeriveInput); 8 | let name = input.ident; 9 | 10 | let expanded = quote! { 11 | impl ::quartz_contract_core::msg::execute::attested::HasUserData for #name { 12 | fn user_data(&self) -> ::quartz_contract_core::state::UserData { 13 | ::quartz_contract_core::msg::execute::attested::user_data_json(self) 14 | } 15 | } 16 | }; 17 | 18 | TokenStream::from(expanded) 19 | } 20 | -------------------------------------------------------------------------------- /crates/contracts/core/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use k256::ecdsa::Error as K256Error; 3 | use quartz_tee_ra::Error as RaVerificationError; 4 | use thiserror::Error; 5 | 6 | #[derive(Error, Debug)] 7 | pub enum Error { 8 | #[error("{0}")] 9 | Std(#[from] StdError), 10 | #[error("{0}")] 11 | RaVerification(#[from] RaVerificationError), 12 | #[error("Signature verification error: {0}")] 13 | SignatureVerification(String), 14 | #[error("Not Secp256K1")] 15 | K256(K256Error), 16 | #[error("invalid session nonce or attempt to reset pub_key")] 17 | BadSessionTransition, 18 | #[error("Invalid FMSPC: {0}")] 19 | InvalidFmspc(String), 20 | #[error("TCB Info query error: {0}")] 21 | TcbInfoQueryError(String), 22 | #[error("DCAP verification query error: {0}")] 23 | DcapVerificationQueryError(String), 24 | #[error("contract address mismatch")] 25 | ContractAddrMismatch, 26 | } 27 | 28 | impl From for Error { 29 | fn from(e: K256Error) -> Self { 30 | Self::K256(e) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/execute.rs: -------------------------------------------------------------------------------- 1 | pub mod attested; 2 | pub mod sequenced; 3 | pub mod session_create; 4 | pub mod session_set_pub_key; 5 | pub mod signed; 6 | 7 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; 8 | 9 | use crate::{ 10 | error::Error, 11 | handler::Handler, 12 | msg::execute::{ 13 | attested::{Attestation, HasUserData}, 14 | Execute, 15 | }, 16 | }; 17 | 18 | impl Handler for Execute 19 | where 20 | A: Handler + HasUserData + Attestation, 21 | { 22 | fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result { 23 | match self { 24 | Execute::SessionCreate(msg) => msg.handle(deps, env, info), 25 | Execute::SessionSetPubKey(msg) => msg.handle(deps, env, info), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/execute/sequenced.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdResult, Uint64}; 2 | 3 | use crate::{ 4 | error::Error, handler::Handler, msg::execute::sequenced::Sequenced, state::SEQUENCE_NUM, 5 | }; 6 | 7 | impl Handler for Sequenced { 8 | fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result { 9 | SEQUENCE_NUM.update(deps.storage, |mut counter| -> StdResult<_> { 10 | counter += Uint64::one(); 11 | Ok(counter) 12 | })?; 13 | 14 | self.0.handle(deps, env, info) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/execute/session_create.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; 2 | 3 | use crate::{ 4 | error::Error, 5 | handler::Handler, 6 | msg::execute::session_create::SessionCreate, 7 | state::{Session, SESSION}, 8 | }; 9 | 10 | impl Handler for SessionCreate { 11 | fn handle(self, deps: DepsMut<'_>, env: &Env, _info: &MessageInfo) -> Result { 12 | // TODO(hu55a1n1): overwrite previous session? 13 | 14 | let addr = deps.api.addr_validate(self.contract())?; 15 | if addr != env.contract.address { 16 | return Err(Error::ContractAddrMismatch); 17 | } 18 | 19 | SESSION 20 | .save(deps.storage, &Session::create(self.nonce())) 21 | .map_err(Error::Std)?; 22 | 23 | Ok(Response::new().add_attribute("action", "session_create")) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/execute/session_set_pub_key.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, Uint64}; 2 | 3 | use crate::{ 4 | error::Error, 5 | handler::Handler, 6 | msg::execute::session_set_pub_key::SessionSetPubKey, 7 | state::{SEQUENCE_NUM, SESSION}, 8 | }; 9 | 10 | impl Handler for SessionSetPubKey { 11 | fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result { 12 | let session = SESSION.load(deps.storage).map_err(Error::Std)?; 13 | let (nonce, pub_key) = self.into_tuple(); 14 | 15 | let session = session 16 | .with_pub_key(nonce, pub_key.clone()) 17 | .ok_or(Error::BadSessionTransition)?; 18 | SESSION.save(deps.storage, &session).map_err(Error::Std)?; 19 | 20 | let sequence_num = Uint64::new(0); 21 | SEQUENCE_NUM 22 | .save(deps.storage, &sequence_num) 23 | .map_err(Error::Std)?; 24 | 25 | Ok(Response::new() 26 | .add_attribute("action", "session_set_pub_key") 27 | .add_attribute("pub_key", HexBinary::from(pub_key).to_hex())) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/execute/signed.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; 2 | 3 | use crate::{ 4 | error::Error, 5 | handler::Handler, 6 | msg::execute::signed::{Auth, MsgVerifier, Signed}, 7 | }; 8 | 9 | impl Handler for Signed 10 | where 11 | M: Handler + MsgVerifier, 12 | A: Auth, 13 | { 14 | fn handle( 15 | self, 16 | mut deps: DepsMut<'_>, 17 | env: &Env, 18 | info: &MessageInfo, 19 | ) -> Result { 20 | let (msg, auth) = self.into_tuple(); 21 | let pub_key = auth.pub_key(); 22 | msg.verify(pub_key, auth.sig())?; 23 | Handler::handle(msg, deps.branch(), env, info) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/contracts/core/src/handler/instantiate.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; 2 | use quartz_tee_ra::Error as RaVerificationError; 3 | 4 | use crate::{ 5 | error::Error, 6 | handler::Handler, 7 | msg::{ 8 | execute::attested::{Attestation, HasUserData}, 9 | instantiate::{CoreInstantiate, Instantiate}, 10 | }, 11 | state::{RawConfig, CONFIG}, 12 | }; 13 | 14 | impl Handler for Instantiate 15 | where 16 | A: Attestation + Handler + HasUserData, 17 | { 18 | fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result { 19 | if self.0.msg().config().mr_enclave() != self.0.attestation().mr_enclave() { 20 | return Err(RaVerificationError::MrEnclaveMismatch.into()); 21 | } 22 | self.0.handle(deps, env, info) 23 | } 24 | } 25 | 26 | impl Handler for CoreInstantiate { 27 | fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result { 28 | CONFIG 29 | .save(deps.storage, &RawConfig::from(self.config().clone())) 30 | .map_err(Error::Std)?; 31 | 32 | Ok(Response::new().add_attribute("action", "instantiate")) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/contracts/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | clippy::checked_conversions, 3 | clippy::panic, 4 | clippy::panic_in_result_fn, 5 | clippy::unwrap_used, 6 | rust_2018_idioms, 7 | unused_lifetimes 8 | )] 9 | #![deny( 10 | trivial_casts, 11 | trivial_numeric_casts, 12 | unused_import_braces, 13 | unused_qualifications, 14 | warnings 15 | )] 16 | #![forbid(unsafe_code)] 17 | 18 | pub mod error; 19 | pub mod handler; 20 | pub mod msg; 21 | pub mod prelude; 22 | pub mod state; 23 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg.rs: -------------------------------------------------------------------------------- 1 | pub mod execute; 2 | pub mod instantiate; 3 | pub mod query; 4 | 5 | use cosmwasm_std::StdError; 6 | pub use execute::{Execute as ExecuteMsg, RawExecute as RawExecuteMsg}; 7 | pub use instantiate::{Instantiate as InstantiateMsg, RawInstantiate as RawInstantiateMsg}; 8 | use serde::Serialize; 9 | 10 | pub trait HasDomainType: From + Serialize { 11 | type DomainType: TryFrom; 12 | } 13 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg/execute.rs: -------------------------------------------------------------------------------- 1 | pub mod attested; 2 | pub mod sequenced; 3 | pub mod session_create; 4 | pub mod session_set_pub_key; 5 | pub mod signed; 6 | 7 | use cosmwasm_schema::cw_serde; 8 | use cosmwasm_std::StdError; 9 | 10 | use crate::msg::{ 11 | execute::{ 12 | attested::{Attested, DefaultAttestation, RawAttested, RawDefaultAttestation}, 13 | session_create::{RawSessionCreate, SessionCreate}, 14 | session_set_pub_key::{RawSessionSetPubKey, SessionSetPubKey}, 15 | }, 16 | HasDomainType, 17 | }; 18 | 19 | #[derive(Clone, Debug, PartialEq)] 20 | pub enum Execute { 21 | SessionCreate(Attested), 22 | SessionSetPubKey(Attested), 23 | } 24 | 25 | #[cw_serde] 26 | pub enum RawExecute { 27 | #[serde(rename = "session_create")] 28 | RawSessionCreate(RawAttested), 29 | #[serde(rename = "session_set_pub_key")] 30 | RawSessionSetPubKey(RawAttested), 31 | } 32 | 33 | impl TryFrom> for Execute 34 | where 35 | RA: HasDomainType, 36 | { 37 | type Error = StdError; 38 | 39 | fn try_from(value: RawExecute) -> Result { 40 | match value { 41 | RawExecute::RawSessionCreate(msg) => { 42 | Ok(Execute::SessionCreate(TryFrom::try_from(msg)?)) 43 | } 44 | RawExecute::RawSessionSetPubKey(msg) => { 45 | Ok(Execute::SessionSetPubKey(TryFrom::try_from(msg)?)) 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl From> for RawExecute 52 | where 53 | RA: HasDomainType, 54 | { 55 | fn from(value: Execute) -> Self { 56 | match value { 57 | Execute::SessionCreate(msg) => RawExecute::RawSessionCreate(From::from(msg)), 58 | Execute::SessionSetPubKey(msg) => RawExecute::RawSessionSetPubKey(From::from(msg)), 59 | } 60 | } 61 | } 62 | 63 | impl HasDomainType for RawExecute 64 | where 65 | RA: HasDomainType, 66 | { 67 | type DomainType = Execute; 68 | } 69 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg/execute/sequenced.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::StdError; 3 | use serde::Serialize; 4 | 5 | use crate::msg::HasDomainType; 6 | 7 | #[derive(Clone, Debug, PartialEq)] 8 | pub struct Sequenced(pub D); 9 | 10 | #[cw_serde] 11 | pub struct RawSequenced(pub R); 12 | 13 | impl HasDomainType for RawSequenced { 14 | type DomainType = Sequenced; 15 | } 16 | 17 | impl TryFrom> for Sequenced { 18 | type Error = StdError; 19 | 20 | fn try_from(value: RawSequenced) -> Result { 21 | Ok(Self(value.0.try_into()?)) 22 | } 23 | } 24 | 25 | impl From> for RawSequenced { 26 | fn from(value: Sequenced) -> Self { 27 | Self(value.0.into()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg/execute/session_create.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::{HexBinary, StdError}; 3 | use sha2::{Digest, Sha256}; 4 | 5 | use crate::{ 6 | msg::{execute::attested::HasUserData, HasDomainType}, 7 | state::{Nonce, UserData}, 8 | }; 9 | 10 | #[derive(Clone, Debug, PartialEq)] 11 | pub struct SessionCreate { 12 | nonce: Nonce, 13 | contract: String, 14 | } 15 | 16 | impl SessionCreate { 17 | pub fn new(nonce: Nonce, contract: String) -> Self { 18 | Self { nonce, contract } 19 | } 20 | 21 | pub fn nonce(&self) -> Nonce { 22 | self.nonce 23 | } 24 | 25 | pub fn contract(&self) -> &str { 26 | self.contract.as_str() 27 | } 28 | } 29 | 30 | #[cw_serde] 31 | pub struct RawSessionCreate { 32 | nonce: HexBinary, 33 | contract: String, 34 | } 35 | 36 | impl TryFrom for SessionCreate { 37 | type Error = StdError; 38 | 39 | fn try_from(value: RawSessionCreate) -> Result { 40 | let nonce = value.nonce.to_array()?; 41 | let contract = value.contract; 42 | Ok(Self { nonce, contract }) 43 | } 44 | } 45 | 46 | impl From for RawSessionCreate { 47 | fn from(value: SessionCreate) -> Self { 48 | Self { 49 | nonce: value.nonce.into(), 50 | contract: value.contract, 51 | } 52 | } 53 | } 54 | 55 | impl HasDomainType for RawSessionCreate { 56 | type DomainType = SessionCreate; 57 | } 58 | 59 | impl HasUserData for SessionCreate { 60 | fn user_data(&self) -> UserData { 61 | let mut hasher = Sha256::new(); 62 | hasher.update( 63 | serde_json::to_string(&RawSessionCreate::from(self.clone())) 64 | .expect("infallible serializer"), 65 | ); 66 | let digest: [u8; 32] = hasher.finalize().into(); 67 | 68 | let mut user_data = [0u8; 64]; 69 | user_data[0..32].copy_from_slice(&digest); 70 | user_data 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg/execute/session_set_pub_key.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::{HexBinary, StdError}; 3 | use sha2::{Digest, Sha256}; 4 | 5 | use crate::{ 6 | msg::{execute::attested::HasUserData, HasDomainType}, 7 | state::{Nonce, UserData}, 8 | }; 9 | 10 | #[derive(Clone, Debug, PartialEq)] 11 | pub struct SessionSetPubKey { 12 | nonce: Nonce, 13 | pub_key: Vec, 14 | } 15 | 16 | impl SessionSetPubKey { 17 | pub fn new(nonce: Nonce, pub_key: Vec) -> Self { 18 | Self { nonce, pub_key } 19 | } 20 | 21 | pub fn into_tuple(self) -> (Nonce, Vec) { 22 | (self.nonce, self.pub_key) 23 | } 24 | } 25 | 26 | #[cw_serde] 27 | pub struct RawSessionSetPubKey { 28 | nonce: HexBinary, 29 | pub_key: HexBinary, 30 | } 31 | 32 | impl RawSessionSetPubKey { 33 | pub fn pub_key(&self) -> &HexBinary { 34 | &self.pub_key 35 | } 36 | } 37 | 38 | impl TryFrom for SessionSetPubKey { 39 | type Error = StdError; 40 | 41 | fn try_from(value: RawSessionSetPubKey) -> Result { 42 | let nonce = value.nonce.to_array()?; 43 | Ok(Self { 44 | nonce, 45 | pub_key: value.pub_key.into(), 46 | }) 47 | } 48 | } 49 | 50 | impl From for RawSessionSetPubKey { 51 | fn from(value: SessionSetPubKey) -> Self { 52 | Self { 53 | nonce: value.nonce.into(), 54 | pub_key: value.pub_key.into(), 55 | } 56 | } 57 | } 58 | 59 | impl HasDomainType for RawSessionSetPubKey { 60 | type DomainType = SessionSetPubKey; 61 | } 62 | 63 | impl HasUserData for SessionSetPubKey { 64 | fn user_data(&self) -> UserData { 65 | let mut hasher = Sha256::new(); 66 | hasher.update( 67 | serde_json::to_string(&RawSessionSetPubKey::from(self.clone())) 68 | .expect("infallible serializer"), 69 | ); 70 | let digest: [u8; 32] = hasher.finalize().into(); 71 | 72 | let mut user_data = [0u8; 64]; 73 | user_data[0..32].copy_from_slice(&digest); 74 | user_data 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /crates/contracts/core/src/msg/query.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/contracts/core/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{ 2 | handler::RawHandler, 3 | msg::{ 4 | execute::RawExecute as QuartzExecuteMsg, 5 | instantiate::RawInstantiate as QuartzInstantiateMsg, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-dcap-verifier" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Standalone CosmWasm smart contract for verifying Intel SGX DCAP attestations that can be called by other contracts. 15 | """ 16 | 17 | exclude = [ 18 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 19 | "contract.wasm", 20 | "hash.txt", 21 | ] 22 | 23 | [lib] 24 | crate-type = ["cdylib", "rlib"] 25 | 26 | [features] 27 | # use library feature to disable all instantiate/execute/query exports 28 | library = [] 29 | 30 | [package.metadata.scripts] 31 | optimize = """docker run --rm -v "$(pwd)":/code \ 32 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ 33 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 34 | cosmwasm/rust-optimizer:0.14.0 35 | """ 36 | 37 | [dependencies] 38 | # external 39 | ciborium.workspace = true 40 | 41 | # cosmos 42 | cosmwasm-schema.workspace = true 43 | cosmwasm-std.workspace = true 44 | 45 | # quartz 46 | quartz-dcap-verifier-msgs.workspace = true 47 | quartz-tee-ra.workspace = true 48 | 49 | # patch indirect deps 50 | getrandom = { version = "0.2.15", features = ["js"] } 51 | 52 | [dev-dependencies] 53 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/README.md: -------------------------------------------------------------------------------- 1 | # CosmWasm smart contract to verify DCAP attestations 2 | 3 | Your personal DCAP detective! This package is a standalone smart contract for verifying DCAP attestations that can be called by other contracts. 4 | 5 | - Thin wrapper for standalone smart contract around the functionality provided in the `quartz-tee-ra` package 6 | - Provides query and execute entry points for attestation checks 7 | 8 | 9 | ## Testing instructions 10 | ``` 11 | wasmd query wasm contract-state smart "$CONTRACT" '{ 12 | "verify_dcap_attestation": { 13 | "quote": { /* ... */ }, 14 | "collateral": { /* ... */ }, 15 | "mrenclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb", 16 | "user_data": "9113b0be77ed5d0d68680ec77206b8d587ed40679b71321ccdd5405e4d54a6820000000000000000000000000000000000000000000000000000000000000000" 17 | } 18 | }' 19 | ``` 20 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/msgs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-dcap-verifier-msgs" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 11 | readme = "README.md" 12 | description = """ 13 | Message and query definitions for the quartz-dcap-verifier contract 14 | """ 15 | 16 | [dependencies] 17 | # cosmos 18 | cosmwasm-schema.workspace = true 19 | cosmwasm-std.workspace = true 20 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/msgs/README.md: -------------------------------------------------------------------------------- 1 | # quartz-dcap-verifier-msgs 2 | 3 | Message and query definitions for the quartz-dcap-verifier contract 4 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/msgs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | use cosmwasm_std::HexBinary; 3 | 4 | #[cw_serde] 5 | pub struct InstantiateMsg; 6 | 7 | #[cw_serde] 8 | pub enum ExecuteMsg {} 9 | 10 | #[cw_serde] 11 | #[derive(QueryResponses)] 12 | pub enum QueryMsg { 13 | /// Verify a DCAP attestation 14 | #[returns(())] 15 | VerifyDcapAttestation { 16 | quote: HexBinary, 17 | collateral: HexBinary, 18 | identities: Option>, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/src/bin/dcap_verifier_schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use quartz_dcap_verifier_msgs::{ExecuteMsg, InstantiateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | query: QueryMsg, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/contracts/dcap-verifier/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny( 2 | warnings, 3 | trivial_casts, 4 | trivial_numeric_casts, 5 | unused_import_braces, 6 | unused_qualifications 7 | )] 8 | #![forbid(unsafe_code)] 9 | 10 | pub mod contract; 11 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --bin schema" 5 | integration-test = "test --lib integration_tests" 6 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | /target 3 | /schema 4 | 5 | # Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) 6 | .cargo-ok 7 | 8 | # Text file backups 9 | **/*.rs.bk 10 | 11 | # macOS 12 | .DS_Store 13 | 14 | # IDEs 15 | *.iml 16 | .idea 17 | 18 | .editorconfig -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-tcbinfo" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Standalone CosmWasm smart contract for storage and verification of TcbInfos for Intel SGX. 15 | """ 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [lib] 20 | crate-type = ["cdylib", "rlib"] 21 | 22 | [features] 23 | # use library feature to disable all instantiate/execute/query exports 24 | library = [] 25 | 26 | [package.metadata.scripts] 27 | optimize = """docker run --rm -v "$(pwd)":/code \ 28 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ 29 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 30 | cosmwasm/optimizer:0.15.0 31 | """ 32 | 33 | [dependencies] 34 | # external 35 | der.workspace = true 36 | hex.workspace = true 37 | p256.workspace = true 38 | schemars.workspace = true 39 | serde.workspace = true 40 | serde_json.workspace = true 41 | thiserror.workspace = true 42 | x509-cert.workspace = true 43 | 44 | # mobilecoin 45 | mc-attestation-verifier.workspace = true 46 | 47 | # cosmos 48 | cosmwasm-schema.workspace = true 49 | cosmwasm-std.workspace = true 50 | cw2.workspace = true 51 | cw-storage-plus.workspace = true 52 | 53 | # quartz 54 | quartz-tee-ra.workspace = true 55 | quartz-tcbinfo-msgs.workspace = true 56 | 57 | # patch indirect deps 58 | getrandom = { version = "0.2.15", features = ["js"] } 59 | 60 | [dev-dependencies] 61 | cw-multi-test = "2.0.0" 62 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/README.md: -------------------------------------------------------------------------------- 1 | # CosmWasm SGX TcbInfo Smart Contract 2 | 3 | Standalone smart contract for storage and verification of `TcbInfo`s for Intel SGX. The contract ensures that 4 | TcbInfos are kept up-to-date so other contracts can query the latest TcbInfo state using the quote's `fmspc` during 5 | remote attestation verification to ensure the attesting enclave setup is up-to-date. 6 | 7 | ## Overview 8 | 9 | The contract provides the following functionalities: 10 | 11 | - Instantiate: Initialize the contract with a root certificate. 12 | - Execute: Store and verify TcbInfo along with the provided certificate and optional timestamp. 13 | - Query: Retrieve the latest TcbInfo using the FMSPC. 14 | 15 | ## Usage (with wasmd) 16 | 17 | - Submit a new `TcbInfo` for a specific `fmspc` 18 | 19 | ```shell 20 | export EXECUTE='{ 21 | "tcb_info": "{\"tcbInfo\":{ /* ... */ },\"signature\":\"647bac99371750892415557b838237839e52b02afe027a43322fe661f4a1a693b04a82717120d74bccf2b3787bf7e9ecbe44caa06e6e532b7a68a21b2765663d\"} 22 | "certificate": "-----BEGIN CERTIFICATE-----\\n /* ... */ \\n-----END CERTIFICATE-----" 23 | }' 24 | wasmd tx wasm execute "$CONTRACT" "$EXECUTE" --from alice --chain-id testing -y 25 | ``` 26 | 27 | - Query the latest `TcbInfo` by `fmspc` 28 | 29 | ```shell 30 | wasmd query wasm contract-state smart "$CONTRACT" '{"get_tcb_info": {"fmspc": "00906ED50000"}}' 31 | ``` 32 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/data/root_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG 6 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0 7 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT 8 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7 9 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB 10 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ 11 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50 12 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV 13 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI 14 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg 15 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI= 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/data/tcb_signer.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG 6 | A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw 7 | b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD 8 | VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv 9 | P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju 10 | ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f 11 | BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz 12 | LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK 13 | QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG 14 | SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj 15 | ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/msgs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-tcbinfo-msgs" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 11 | readme = "README.md" 12 | description = """ 13 | Message and query definitions for the quartz-tcbinfo contract 14 | """ 15 | 16 | [dependencies] 17 | # external 18 | 19 | # cosmos 20 | cosmwasm-schema.workspace = true 21 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/msgs/README.md: -------------------------------------------------------------------------------- 1 | # quartz-tcbinfo-msgs 2 | 3 | Message and query definitions for the quartz-tcbinfo contract 4 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/msgs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | 3 | #[cw_serde] 4 | pub struct InstantiateMsg { 5 | pub root_cert: String, 6 | } 7 | 8 | #[cw_serde] 9 | pub struct ExecuteMsg { 10 | pub tcb_info: String, 11 | pub certificate: String, 12 | pub time: Option, 13 | } 14 | 15 | #[cw_serde] 16 | #[derive(QueryResponses)] 17 | pub enum QueryMsg { 18 | #[returns(GetTcbInfoResponse)] 19 | GetTcbInfo { fmspc: String }, 20 | } 21 | 22 | #[cw_serde] 23 | pub struct GetTcbInfoResponse { 24 | pub tcb_info: String, 25 | } 26 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/src/bin/tcbinfo_schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use quartz_tcbinfo_msgs::{ExecuteMsg, InstantiateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | query: QueryMsg, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Unauthorized")] 10 | Unauthorized {}, 11 | // Add any other custom errors you like here. 12 | // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. 13 | #[error("Certificate verification failed")] 14 | CertificateVerificationError, 15 | #[error("failed to verify tcbinfo")] 16 | TcbInfoVerificationError, 17 | #[error("invalid public key")] 18 | PublicKeyReadError, 19 | #[error("invalid date and time")] 20 | DateTimeReadError, 21 | #[error("invalid tcbinfo")] 22 | TcbInfoReadError, 23 | } 24 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{ 2 | to_json_binary, Addr, CosmosMsg, CustomQuery, Querier, QuerierWrapper, StdResult, WasmMsg, 3 | WasmQuery, 4 | }; 5 | use quartz_tcbinfo_msgs::{ExecuteMsg, GetTcbInfoResponse, QueryMsg}; 6 | use schemars::JsonSchema; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | const FMSPC: &str = "00606a000000"; 10 | 11 | /// CwTemplateContract is a wrapper around Addr that provides a lot of helpers 12 | /// for working with this. 13 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] 14 | pub struct CwTemplateContract(pub Addr); 15 | 16 | impl CwTemplateContract { 17 | pub fn addr(&self) -> Addr { 18 | self.0.clone() 19 | } 20 | 21 | pub fn call>(&self, msg: T) -> StdResult { 22 | let msg = to_json_binary(&msg.into())?; 23 | Ok(WasmMsg::Execute { 24 | contract_addr: self.addr().into(), 25 | msg, 26 | funds: vec![], 27 | } 28 | .into()) 29 | } 30 | 31 | // Get Count 32 | pub fn get_tcbinfo(&self, querier: &Q) -> StdResult 33 | where 34 | Q: Querier, 35 | T: Into, 36 | CQ: CustomQuery, 37 | { 38 | let msg = QueryMsg::GetTcbInfo { 39 | fmspc: FMSPC.to_string(), 40 | }; 41 | let query = WasmQuery::Smart { 42 | contract_addr: self.addr().into(), 43 | msg: to_json_binary(&msg)?, 44 | } 45 | .into(); 46 | let res: GetTcbInfoResponse = QuerierWrapper::::new(querier).query(&query)?; 47 | Ok(res) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod helpers; 4 | pub mod integration_tests; 5 | pub mod state; 6 | pub use crate::error::ContractError; 7 | -------------------------------------------------------------------------------- /crates/contracts/tcbinfo/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cw_storage_plus::{Item, Map}; 3 | 4 | pub type Fmspc = [u8; 6]; 5 | 6 | #[cw_serde] 7 | pub struct TcbInfo { 8 | pub info: String, 9 | // pub certificate: String, 10 | } 11 | 12 | pub const DATABASE: Map = Map::new("state"); 13 | pub const ROOT_CERTIFICATE: Item = Item::new("root_certificate"); 14 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-tee-ra" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Internal CosmWasm library for handling Intel SGX DCAP remote attestations. 15 | """ 16 | 17 | [dependencies] 18 | # external 19 | der.workspace = true 20 | hex-literal.workspace = true 21 | serde.workspace = true 22 | thiserror.workspace = true 23 | x509-cert.workspace = true 24 | x509-parser.workspace = true 25 | 26 | # mobilecoin 27 | mc-attestation-verifier.workspace = true 28 | mc-sgx-dcap-types.workspace = true 29 | 30 | [dev-dependencies] 31 | hex = "0.4.3" 32 | mc-sgx-dcap-types.workspace = true 33 | mc-sgx-core-types.workspace = true 34 | mc-sgx-dcap-sys-types.workspace = true 35 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/DcapRootCACert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG 6 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0 7 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT 8 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7 9 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB 10 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ 11 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50 12 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV 13 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI 14 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg 15 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI= 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/hw_quote.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/crates/contracts/tee-ra/data/hw_quote.dat -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/leaf_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEjzCCBDSgAwIBAgIVAPtJxlxRlleZOb/spRh9U8K7AT/3MAoGCCqGSM49BAMC 3 | MHExIzAhBgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQK 4 | DBFJbnRlbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNV 5 | BAgMAkNBMQswCQYDVQQGEwJVUzAeFw0yMjA2MTMyMTQ2MzRaFw0yOTA2MTMyMTQ2 6 | MzRaMHAxIjAgBgNVBAMMGUludGVsIFNHWCBQQ0sgQ2VydGlmaWNhdGUxGjAYBgNV 7 | BAoMEUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkG 8 | A1UECAwCQ0ExCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE 9 | j/Ee1lkGJofDX745Ks5qxqu7Mk7Mqcwkx58TCSTsabRCSvobSl/Ts8b0dltKUW3j 10 | qRd+SxnPEWJ+jUw+SpzwWaOCAqgwggKkMB8GA1UdIwQYMBaAFNDoqtp11/kuSReY 11 | PHsUZdDV8llNMGwGA1UdHwRlMGMwYaBfoF2GW2h0dHBzOi8vYXBpLnRydXN0ZWRz 12 | ZXJ2aWNlcy5pbnRlbC5jb20vc2d4L2NlcnRpZmljYXRpb24vdjMvcGNrY3JsP2Nh 13 | PXByb2Nlc3NvciZlbmNvZGluZz1kZXIwHQYDVR0OBBYEFKy9gk624HzNnDyCw7QW 14 | nhmVfE31MA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMIIB1AYJKoZIhvhN 15 | AQ0BBIIBxTCCAcEwHgYKKoZIhvhNAQ0BAQQQ36FQl3ntUr3KUwbEFvmRGzCCAWQG 16 | CiqGSIb4TQENAQIwggFUMBAGCyqGSIb4TQENAQIBAgERMBAGCyqGSIb4TQENAQIC 17 | AgERMBAGCyqGSIb4TQENAQIDAgECMBAGCyqGSIb4TQENAQIEAgEEMBAGCyqGSIb4 18 | TQENAQIFAgEBMBEGCyqGSIb4TQENAQIGAgIAgDAQBgsqhkiG+E0BDQECBwIBBjAQ 19 | BgsqhkiG+E0BDQECCAIBADAQBgsqhkiG+E0BDQECCQIBADAQBgsqhkiG+E0BDQEC 20 | CgIBADAQBgsqhkiG+E0BDQECCwIBADAQBgsqhkiG+E0BDQECDAIBADAQBgsqhkiG 21 | +E0BDQECDQIBADAQBgsqhkiG+E0BDQECDgIBADAQBgsqhkiG+E0BDQECDwIBADAQ 22 | BgsqhkiG+E0BDQECEAIBADAQBgsqhkiG+E0BDQECEQIBCzAfBgsqhkiG+E0BDQEC 23 | EgQQERECBAGABgAAAAAAAAAAADAQBgoqhkiG+E0BDQEDBAIAADAUBgoqhkiG+E0B 24 | DQEEBAYAkG7VAAAwDwYKKoZIhvhNAQ0BBQoBADAKBggqhkjOPQQDAgNJADBGAiEA 25 | 1XJi0ht4hw8YtC6E4rYscp9bF+7UOhVGeKePA5TW2FQCIQCIUAaewOuWOIvstZN4 26 | V8Zu8NFCC4vFg+cZqO6QfezEaA== 27 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/processor_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICmDCCAj6gAwIBAgIVANDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMC 3 | MGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD 4 | b3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw 5 | CQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHExIzAh 6 | BgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQKDBFJbnRl 7 | bCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNB 8 | MQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL9q+NMp2IOg 9 | tdl1bk/uWZ5+TGQm8aCi8z78fs+fKCQ3d+uDzXnVTAT2ZhDCifyIuJwvN3wNBp9i 10 | HBSSMJMJrBOjgbswgbgwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqww 11 | UgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNl 12 | cnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFNDo 13 | qtp11/kuSReYPHsUZdDV8llNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG 14 | AQH/AgEAMAoGCCqGSM49BAMCA0gAMEUCIQCJgTbtVqOyZ1m3jqiAXM6QYa6r5sWS 15 | 4y/G7y8uIJGxdwIgRqPvBSKzzQagBLQq5s5A70pdoiaRJ8z/0uDz4NgV91k= 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/processor_crl.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/crates/contracts/tee-ra/data/processor_crl.der -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/qe_identity.json: -------------------------------------------------------------------------------- 1 | {"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-07-12T20:48:25Z","nextUpdate":"2023-08-11T20:48:25Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"953add69a564b80c43adb9c9dbc888da81aad8af240cd7dfd751f0209d262a71d9240603a528cb766e9fc3278722e59a43f2a2e43b55c776a7b48acbe8cd61a3"} -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/root_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG 6 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0 7 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT 8 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7 9 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB 10 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ 11 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50 12 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV 13 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI 14 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg 15 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI= 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/root_crl.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/crates/contracts/tee-ra/data/root_crl.der -------------------------------------------------------------------------------- /crates/contracts/tee-ra/data/tcb_signer.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG 6 | A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw 7 | b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD 8 | VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv 9 | P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju 10 | ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f 11 | BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz 12 | LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK 13 | QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG 14 | SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj 15 | ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/src/intel_sgx.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | pub mod dcap; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum Error { 7 | #[error("Specified user data does not match the report")] 8 | UserDataMismatch, 9 | #[error("Specified MRENCLAVE does not match the report")] 10 | MrEnclaveMismatch, 11 | #[error("DCAP specific error: {0:?}")] 12 | Dcap(Box>), 13 | } 14 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/src/intel_sgx/dcap/mc_attest_verifier.rs: -------------------------------------------------------------------------------- 1 | // Trimmed down version of the `mc_attest_verifier` crate (for DCAP verification only) -> 2 | // https://github.com/hu55a1n1/mobilecoin/blob/a1eba594f5b1ebe3d2b0736cf76d38120f84a4a0/attest/verifier 3 | 4 | pub mod dcap; 5 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/src/intel_sgx/dcap/mc_attest_verifier/dcap.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The MobileCoin Foundation 2 | 3 | //! Verify the contents of a Quote3. 4 | 5 | use der::DateTime; 6 | use mc_attestation_verifier::{ 7 | Evidence, EvidenceValue, EvidenceVerifier, TrustedIdentity, VerificationOutput, Verifier, 8 | }; 9 | 10 | use super::super::certificate_chain::TlsCertificateChainVerifier; 11 | 12 | #[derive(Debug)] 13 | pub struct DcapVerifier { 14 | verifier: EvidenceVerifier, 15 | } 16 | 17 | pub type DcapVerifierOutput = EvidenceValue; 18 | 19 | impl DcapVerifier { 20 | /// Create a new instance of the DcapVerifier. 21 | /// 22 | /// # Arguments 23 | /// * `trusted_identities` - The allowed identities that can be used in an 24 | /// enclave. Verification will succeed if any of these match. 25 | /// * `time` - The time to use to verify the validity of the certificates 26 | /// and collateral. If time is provided, verification will fail if this 27 | /// time is before or after any of the validity periods. Otherwise, time 28 | /// validation of certificates will be skipped. 29 | pub fn new(trusted_identities: I, time: impl Into>) -> Self 30 | where 31 | I: IntoIterator, 32 | ID: Into, 33 | { 34 | let certificate_verifier = TlsCertificateChainVerifier; 35 | let verifier = EvidenceVerifier::new(certificate_verifier, trusted_identities, time); 36 | Self { verifier } 37 | } 38 | 39 | /// Verify the `evidence` 40 | pub fn verify(&self, evidence: &Evidence>) -> VerificationOutput { 41 | self.verifier.verify(evidence) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/contracts/tee-ra/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | clippy::checked_conversions, 3 | clippy::panic, 4 | clippy::panic_in_result_fn, 5 | clippy::unwrap_used, 6 | rust_2018_idioms, 7 | unused_lifetimes 8 | )] 9 | #![deny( 10 | trivial_casts, 11 | trivial_numeric_casts, 12 | unused_import_braces, 13 | unused_qualifications, 14 | warnings 15 | )] 16 | // FIXME(hu55a1n1) - uncomment once we have better wrappers for FFI structs and ctors 17 | // #![forbid(unsafe_code)] 18 | 19 | pub mod intel_sgx; 20 | 21 | pub use intel_sgx::{dcap::verify as verify_dcap_attestation, Error}; 22 | -------------------------------------------------------------------------------- /crates/enclave/core/.gitignore: -------------------------------------------------------------------------------- 1 | quartz.sig 2 | -------------------------------------------------------------------------------- /crates/enclave/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-enclave-core" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "hardware-support", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | A Rust framework for building Intel SGX enclaves managed by a CosmWasm contract. 15 | """ 16 | 17 | [features] 18 | mock-sgx = ["quartz-contract-core/mock-sgx"] 19 | 20 | [dependencies] 21 | # external 22 | anyhow.workspace = true 23 | async-trait.workspace = true 24 | displaydoc.workspace = true 25 | futures-util.workspace = true 26 | hex.workspace = true 27 | k256 = { workspace = true, features = ["pem", "serde"] } 28 | log.workspace = true 29 | rand.workspace = true 30 | reqwest = { workspace = true, features = ["blocking"] } 31 | serde.workspace = true 32 | serde_json.workspace = true 33 | tonic.workspace = true 34 | tokio.workspace = true 35 | urlencoding.workspace = true 36 | 37 | # mobilecoin 38 | mc-sgx-dcap-sys-types.workspace = true 39 | 40 | # cosmos 41 | cosmrs.workspace = true 42 | tendermint.workspace = true 43 | tendermint-light-client.workspace = true 44 | tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] } 45 | 46 | # quartz 47 | cw-client.workspace = true 48 | quartz-cw-proof.workspace = true 49 | quartz-contract-core.workspace = true 50 | quartz-proto.workspace = true 51 | quartz-tm-prover.workspace = true 52 | quartz-tee-ra.workspace = true 53 | quartz-tm-stateless-verifier.workspace = true 54 | -------------------------------------------------------------------------------- /crates/enclave/core/README.md: -------------------------------------------------------------------------------- 1 | ## Quartz enclave 2 | 3 | ### Enclave usage 4 | 5 | ```bash 6 | gramine-sgx-gen-private-key 7 | 8 | CARGO_TARGET_DIR=./target cargo build --release 9 | 10 | gramine-manifest \ 11 | -Dlog_level="error" \ 12 | -Dhome=${HOME} \ 13 | -Darch_libdir="/lib/$(gcc -dumpmachine)" \ 14 | -Dra_type="dcap" \ 15 | -Dquartz_dir="$(pwd)" \ 16 | quartz.manifest.template quartz.manifest 17 | 18 | gramine-sgx-sign --manifest quartz.manifest --output quartz.manifest.sgx 19 | gramine-sgx ./quartz 20 | ``` 21 | 22 | ### CLI usage 23 | 24 | ```bash 25 | cargo run -- --chain-id testing \ 26 | --trusted-height 1 \ 27 | --trusted-hash "A1D115BA3A5E9FCC12ED68A9D8669159E9085F6F96EC26619F5C7CEB4EE02869" 28 | ``` 29 | -------------------------------------------------------------------------------- /crates/enclave/core/data/qe_identity.json: -------------------------------------------------------------------------------- 1 | {"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2023-07-12T20:48:25Z","nextUpdate":"2023-08-11T20:48:25Z","tcbEvaluationDataNumber":15,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00615"]},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00202","INTEL-SA-00219","INTEL-SA-00293","INTEL-SA-00334","INTEL-SA-00477","INTEL-SA-00615"]}]},"signature":"953add69a564b80c43adb9c9dbc888da81aad8af240cd7dfd751f0209d262a71d9240603a528cb766e9fc3278722e59a43f2a2e43b55c776a7b48acbe8cd61a3"} -------------------------------------------------------------------------------- /crates/enclave/core/data/root_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG 6 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0 7 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT 8 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7 9 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB 10 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ 11 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50 12 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV 13 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI 14 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg 15 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI= 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /crates/enclave/core/data/root_crl.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/crates/enclave/core/data/root_crl.der -------------------------------------------------------------------------------- /crates/enclave/core/data/tcb_signer.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw 3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv 4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ 5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG 6 | A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw 7 | b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD 8 | VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv 9 | P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju 10 | ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f 11 | BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz 12 | LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK 13 | QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG 14 | SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj 15 | ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /crates/enclave/core/src/event.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Error as AnyhowError}; 2 | use cosmrs::AccountId; 3 | use tendermint_rpc::event::Event as TmEvent; 4 | 5 | use crate::{chain_client::ChainClient, handler::Handler}; 6 | 7 | /// An event wrapper used to parse the contract address for internal handling. 8 | #[derive(Clone, Debug)] 9 | pub struct QuartzEvent { 10 | pub contract: AccountId, 11 | inner: E, 12 | } 13 | 14 | impl TryFrom for QuartzEvent 15 | where 16 | E: TryFrom, 17 | { 18 | type Error = AnyhowError; 19 | 20 | fn try_from(event: TmEvent) -> Result { 21 | let Some(events) = &event.events else { 22 | return Err(anyhow!("no events in tx")); 23 | }; 24 | 25 | let contract = events 26 | .get("execute._contract_address") 27 | .ok_or_else(|| anyhow!("missing execute._contract_address in events"))? 28 | .first() 29 | .ok_or_else(|| anyhow!("execute._contract_address is empty"))? 30 | .parse::() 31 | .map_err(|e| anyhow!("failed to parse contract address: {}", e))?; 32 | 33 | Ok(QuartzEvent { 34 | contract, 35 | inner: event.try_into()?, 36 | }) 37 | } 38 | } 39 | 40 | #[async_trait::async_trait] 41 | impl Handler for QuartzEvent 42 | where 43 | C: ChainClient, 44 | E: Handler, 45 | { 46 | type Error = AnyhowError; 47 | type Response = >::Response; 48 | 49 | async fn handle(self, ctx: &C) -> Result { 50 | self.inner.handle(ctx).await 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/enclave/core/src/grpc.rs: -------------------------------------------------------------------------------- 1 | //! gRPC service implementation for core enclave requests (handshake) 2 | use cosmrs::AccountId; 3 | use quartz_proto::quartz::{ 4 | core_server::Core, InstantiateRequest, InstantiateResponse, SessionCreateRequest, 5 | SessionCreateResponse, SessionSetPubKeyRequest, SessionSetPubKeyResponse, 6 | }; 7 | use tonic::{Request, Response, Status}; 8 | 9 | use crate::{ 10 | attestor::Attestor, handler::Handler, key_manager::KeyManager, store::Store, DefaultEnclave, 11 | }; 12 | 13 | #[async_trait::async_trait] 14 | impl Handler for Request 15 | where 16 | T: Handler, 17 | C: Send + Sync, 18 | { 19 | type Error = T::Error; 20 | type Response = Response; 21 | 22 | async fn handle(self, ctx: &C) -> Result { 23 | let request = self.into_inner(); 24 | let response = request.handle(ctx).await?; 25 | Ok(Response::new(response)) 26 | } 27 | } 28 | 29 | #[async_trait::async_trait] 30 | impl Core for DefaultEnclave 31 | where 32 | C: Send + Sync + 'static, 33 | A: Attestor + Clone, 34 | K: KeyManager + Clone, 35 | S: Store + Clone, 36 | { 37 | async fn instantiate( 38 | &self, 39 | request: Request, 40 | ) -> Result, Status> { 41 | request.handle(self).await 42 | } 43 | 44 | async fn session_create( 45 | &self, 46 | request: Request, 47 | ) -> Result, Status> { 48 | request.handle(self).await 49 | } 50 | 51 | async fn session_set_pub_key( 52 | &self, 53 | request: Request, 54 | ) -> Result, Status> { 55 | request.handle(self).await 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/enclave/core/src/handler/instantiate.rs: -------------------------------------------------------------------------------- 1 | use quartz_contract_core::msg::{execute::attested::Attested, instantiate::CoreInstantiate}; 2 | use quartz_proto::quartz::{ 3 | InstantiateRequest as RawInstantiateRequest, InstantiateResponse as RawInstantiateResponse, 4 | }; 5 | use tonic::Status; 6 | 7 | use crate::{ 8 | attestor::Attestor, 9 | handler::{Handler, A, RA}, 10 | store::Store, 11 | types::InstantiateResponse, 12 | Enclave, 13 | }; 14 | 15 | #[async_trait::async_trait] 16 | impl Handler for RawInstantiateRequest { 17 | type Error = Status; 18 | type Response = RawInstantiateResponse; 19 | 20 | async fn handle(self, ctx: &E) -> Result { 21 | // create `CoreInstantiate` msg and attest to it 22 | let config = ctx 23 | .store() 24 | .await 25 | .get_config() 26 | .await 27 | .map_err(|e| Status::internal(e.to_string()))? 28 | .ok_or_else(|| Status::not_found("config not found"))?; 29 | let msg = CoreInstantiate::new(config); 30 | let attestation = ctx 31 | .attestor() 32 | .await 33 | .attestation(msg.clone()) 34 | .map_err(|e| Status::internal(e.to_string()))?; 35 | let attested_msg = Attested::new(msg, attestation); 36 | 37 | // return response with attested `CoreInstantiate` msg 38 | let response: InstantiateResponse, RA> = InstantiateResponse::new(attested_msg); 39 | Ok(response.into()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/enclave/core/src/key_manager.rs: -------------------------------------------------------------------------------- 1 | pub mod default; 2 | pub mod shared; 3 | 4 | /// A trait defining the public key management functionality within the enclave. 5 | /// 6 | /// The [`KeyManager`] trait is responsible for exposing the enclave's public key, which is stored 7 | /// on-chain as part of the handshake. This can be used by users to encrypt their requests so that 8 | /// they're only seen by the enclave. 9 | /// 10 | /// The public key is expected to be convertible into a byte vector (`Vec`) for easy 11 | /// serialization and on-chain storage. 12 | #[async_trait::async_trait] 13 | pub trait KeyManager: Send + Sync + 'static { 14 | /// The public key type for the KeyManager. 15 | /// 16 | /// This type must be convertible into a vector of bytes (`Vec`). 17 | type PubKey: Into>; 18 | 19 | /// Returns the enclave public key. 20 | async fn pub_key(&self) -> Self::PubKey; 21 | } 22 | -------------------------------------------------------------------------------- /crates/enclave/core/src/key_manager/default.rs: -------------------------------------------------------------------------------- 1 | use k256::ecdsa::{SigningKey, VerifyingKey}; 2 | use log::{debug, info}; 3 | 4 | use crate::key_manager::KeyManager; 5 | 6 | /// A default secp256k1 key-manager. 7 | #[derive(Clone)] 8 | pub struct DefaultKeyManager { 9 | pub sk: SigningKey, 10 | } 11 | 12 | impl Default for DefaultKeyManager { 13 | fn default() -> Self { 14 | info!("Creating new default key manager with random signing key"); 15 | Self { 16 | sk: SigningKey::random(&mut rand::thread_rng()), 17 | } 18 | } 19 | } 20 | 21 | #[async_trait::async_trait] 22 | impl KeyManager for DefaultKeyManager { 23 | type PubKey = PubKey; 24 | 25 | async fn pub_key(&self) -> Self::PubKey { 26 | debug!("Retrieving public key from key manager"); 27 | PubKey(self.sk.clone().into()) 28 | } 29 | } 30 | 31 | #[derive(Clone, Debug)] 32 | pub struct PubKey(VerifyingKey); 33 | 34 | impl From for Vec { 35 | fn from(value: PubKey) -> Self { 36 | value.0.to_sec1_bytes().into() 37 | } 38 | } 39 | 40 | impl From for VerifyingKey { 41 | fn from(value: PubKey) -> Self { 42 | value.0 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/enclave/core/src/key_manager/shared.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use tokio::sync::{RwLock, RwLockReadGuard}; 4 | 5 | use crate::key_manager::KeyManager; 6 | 7 | /// A thread-safe wrapper for a key-manager. 8 | #[derive(Clone, Debug)] 9 | pub struct SharedKeyManager { 10 | pub inner: Arc>, 11 | } 12 | 13 | impl SharedKeyManager { 14 | pub fn wrapping(key_manager: K) -> Self { 15 | Self { 16 | inner: Arc::new(RwLock::new(key_manager)), 17 | } 18 | } 19 | 20 | pub async fn read_lock(&self) -> RwLockReadGuard<'_, K> { 21 | self.inner.read().await 22 | } 23 | } 24 | 25 | #[async_trait::async_trait] 26 | impl KeyManager for SharedKeyManager { 27 | type PubKey = K::PubKey; 28 | 29 | async fn pub_key(&self) -> Self::PubKey { 30 | self.inner.read().await.pub_key().await 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/enclave/core/src/store.rs: -------------------------------------------------------------------------------- 1 | use quartz_contract_core::state::{Config, Nonce}; 2 | 3 | pub mod default; 4 | 5 | /// A trait representing a key-value store for managing core enclave state. 6 | /// 7 | /// The [`Store`] trait defines an asynchronous interface for reading and writing 8 | /// various pieces of state that are essential to the handshake protocol. 9 | #[async_trait::async_trait] 10 | pub trait Store: Send + Sync + 'static { 11 | /// The type representing the contract that the store manages. 12 | type Contract: Send + Sync; 13 | /// The error type returned by store operations. 14 | type Error: ToString + Send + Sync; 15 | 16 | /// Retrieves the current enclave configuration. 17 | async fn get_config(&self) -> Result, Self::Error>; 18 | 19 | /// Sets a new configuration for the enclave. 20 | async fn set_config(&self, config: Config) -> Result, Self::Error>; 21 | 22 | /// Retrieves the contract associated with the enclave. 23 | async fn get_contract(&self) -> Result, Self::Error>; 24 | 25 | /// Sets the contract associated with the enclave. 26 | async fn set_contract( 27 | &self, 28 | contract: Self::Contract, 29 | ) -> Result, Self::Error>; 30 | 31 | /// Retrieves the current nonce. 32 | async fn get_nonce(&self) -> Result, Self::Error>; 33 | 34 | /// Sets a new nonce for replay protection. 35 | async fn set_nonce(&self, nonce: Nonce) -> Result, Self::Error>; 36 | 37 | /// Retrieves the current sequence number. 38 | async fn get_seq_num(&self) -> Result; 39 | 40 | /// Increments the sequence number by the given count. 41 | async fn inc_seq_num(&self, count: usize) -> Result; 42 | } 43 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | 3 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-cw-proof" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Merkle proofs of CosmWasm contract state. This crate contains proof types and a verifier implementation. 15 | """ 16 | 17 | [dependencies] 18 | # external 19 | displaydoc.workspace = true 20 | prost.workspace = true 21 | serde.workspace = true 22 | serde_with.workspace = true 23 | 24 | # cosmos 25 | cosmrs.workspace = true 26 | ics23.workspace = true 27 | tendermint.workspace = true 28 | tendermint-rpc.workspace = true 29 | 30 | [dev-dependencies] 31 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/README.md: -------------------------------------------------------------------------------- 1 | # quartz-cw-proof 2 | 3 | Merkle proofs of CosmWasm contract state. This crate contains proof types and a verifier implementation. 4 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/error.rs: -------------------------------------------------------------------------------- 1 | use displaydoc::Display; 2 | 3 | #[derive(Clone, Debug, Display)] 4 | pub enum ProofError { 5 | /// failed to decode commitment proof 6 | CommitmentProofDecodingFailed, 7 | /// empty merkle root 8 | EmptyMerkleRoot, 9 | /// empty verified value 10 | EmptyVerifiedValue, 11 | /// invalid merkle proof 12 | InvalidMerkleProof, 13 | /// proof verification failed 14 | VerificationFailure, 15 | } 16 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![forbid(unsafe_code)] 3 | #![warn( 4 | clippy::checked_conversions, 5 | clippy::panic, 6 | clippy::panic_in_result_fn, 7 | clippy::unwrap_used, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | rust_2018_idioms, 11 | unused_lifetimes, 12 | unused_import_braces, 13 | unused_qualifications 14 | )] 15 | 16 | extern crate alloc; 17 | 18 | pub mod error; 19 | pub mod proof; 20 | pub mod verifier; 21 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/proof/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use ics23::CommitmentProof; 4 | use tendermint::merkle::proof::ProofOps; 5 | 6 | use crate::error::ProofError; 7 | 8 | pub mod cw; 9 | pub mod key; 10 | pub mod prefix; 11 | 12 | // Copied from hermes 13 | pub fn convert_tm_to_ics_merkle_proof( 14 | tm_proof: &ProofOps, 15 | ) -> Result, ProofError> { 16 | let mut proofs = Vec::new(); 17 | 18 | for op in &tm_proof.ops { 19 | let mut parsed = CommitmentProof { proof: None }; 20 | 21 | prost::Message::merge(&mut parsed, op.data.as_slice()) 22 | .map_err(|_| ProofError::CommitmentProofDecodingFailed)?; 23 | 24 | proofs.push(parsed); 25 | } 26 | 27 | Ok(proofs) 28 | } 29 | 30 | pub trait Proof { 31 | type Key; 32 | type Value; 33 | type ProofOps; 34 | 35 | fn verify(&self, root: Vec) -> Result<(), ProofError>; 36 | } 37 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/proof/prefix.rs: -------------------------------------------------------------------------------- 1 | pub trait ConstPrefix { 2 | const PREFIX: &'static str; 3 | } 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct PrefixWasm; 7 | 8 | impl ConstPrefix for PrefixWasm { 9 | const PREFIX: &'static str = "wasm"; 10 | } 11 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/verifier/cw.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::Cow, vec::Vec}; 2 | 3 | use ics23::CommitmentProof; 4 | 5 | use crate::{ 6 | error::ProofError, 7 | verifier::{ics23::Ics23MembershipVerifier, multi::MultiVerifier, Verifier}, 8 | }; 9 | 10 | type Key = Vec; 11 | type Value<'a> = Cow<'a, [u8]>; 12 | 13 | #[derive(Clone, Debug)] 14 | pub struct CwVerifier<'a>(MultiVerifier>, 2>); 15 | 16 | impl CwVerifier<'_> { 17 | pub fn verify( 18 | &self, 19 | proofs: &[CommitmentProof; 2], 20 | #[allow(clippy::ptr_arg)] root: &Vec, // TODO(hu55a1n1): fix this using `Cow` types 21 | keys: &[Vec; 2], 22 | value: &[u8], 23 | ) -> Result<(), ProofError> { 24 | if root.is_empty() { 25 | return Err(ProofError::EmptyMerkleRoot); 26 | } 27 | 28 | self.0 29 | .verify_against_root(proofs, keys, &Cow::Borrowed(value), root)? 30 | .then_some(()) 31 | .ok_or(ProofError::VerificationFailure) 32 | } 33 | } 34 | 35 | impl Default for CwVerifier<'_> { 36 | fn default() -> Self { 37 | let mv = MultiVerifier::new([ 38 | Ics23MembershipVerifier::new(ics23::iavl_spec()), 39 | Ics23MembershipVerifier::new(ics23::tendermint_spec()), 40 | ]); 41 | Self(mv) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/verifier/ics23.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::marker::PhantomData; 3 | 4 | use ics23::{ 5 | calculate_existence_root, commitment_proof::Proof, verify_membership, CommitmentProof, 6 | ProofSpec, 7 | }; 8 | 9 | use crate::{error::ProofError, verifier::Verifier}; 10 | 11 | #[derive(Clone, Debug)] 12 | pub struct Ics23MembershipVerifier { 13 | spec: ProofSpec, 14 | _phantom: PhantomData<(K, V)>, 15 | } 16 | 17 | impl Ics23MembershipVerifier { 18 | pub fn new(spec: ProofSpec) -> Self { 19 | Self { 20 | spec, 21 | _phantom: Default::default(), 22 | } 23 | } 24 | } 25 | 26 | impl Verifier for Ics23MembershipVerifier 27 | where 28 | K: AsRef<[u8]>, 29 | V: AsRef<[u8]>, 30 | { 31 | type Proof = CommitmentProof; 32 | type Root = Vec; 33 | type Key = K; 34 | type Value = V; 35 | type Error = ProofError; 36 | 37 | fn verify( 38 | &self, 39 | commitment_proof: &Self::Proof, 40 | key: &Self::Key, 41 | value: &Self::Value, 42 | ) -> Result { 43 | if value.as_ref().is_empty() { 44 | return Err(ProofError::EmptyVerifiedValue); 45 | } 46 | 47 | let Some(Proof::Exist(existence_proof)) = &commitment_proof.proof else { 48 | return Err(ProofError::InvalidMerkleProof); 49 | }; 50 | 51 | let root = calculate_existence_root::(existence_proof) 52 | .map_err(|_| ProofError::InvalidMerkleProof)?; 53 | 54 | if !verify_membership::( 55 | commitment_proof, 56 | &self.spec, 57 | &root, 58 | key.as_ref(), 59 | value.as_ref(), 60 | ) { 61 | return Err(ProofError::VerificationFailure); 62 | } 63 | 64 | Ok(root) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/verifier/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cw; 2 | pub mod ics23; 3 | pub mod multi; 4 | 5 | trait Verifier { 6 | type Proof; 7 | type Root: Eq; 8 | type Key; 9 | type Value; 10 | type Error; 11 | 12 | fn verify( 13 | &self, 14 | proof: &Self::Proof, 15 | key: &Self::Key, 16 | value: &Self::Value, 17 | ) -> Result; 18 | 19 | fn verify_against_root( 20 | &self, 21 | proof: &Self::Proof, 22 | key: &Self::Key, 23 | value: &Self::Value, 24 | root: &Self::Root, 25 | ) -> Result { 26 | let found_root = self.verify(proof, key, value)?; 27 | Ok(root == &found_root) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/enclave/cw-proof/src/verifier/multi.rs: -------------------------------------------------------------------------------- 1 | use crate::verifier::Verifier; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct MultiVerifier { 5 | verifiers: [V; N], 6 | } 7 | 8 | impl MultiVerifier { 9 | pub fn new(verifiers: [V; N]) -> Self { 10 | assert!(N > 0, "cannot create empty multi-verifiers"); 11 | Self { verifiers } 12 | } 13 | } 14 | 15 | impl Verifier for MultiVerifier 16 | where 17 | V: Verifier, 18 | V::Root: Into + Clone, 19 | V::Value: Clone, 20 | { 21 | type Proof = [V::Proof; N]; 22 | type Root = V::Root; 23 | type Key = [V::Key; N]; 24 | type Value = V::Value; 25 | type Error = V::Error; 26 | 27 | fn verify( 28 | &self, 29 | proofs: &Self::Proof, 30 | keys: &Self::Key, 31 | value: &Self::Value, 32 | ) -> Result { 33 | let mut root = None; 34 | let mut value: V::Value = value.clone(); 35 | 36 | for (idx, verifier) in self.verifiers.iter().enumerate() { 37 | let proof = &proofs[idx]; 38 | let key = &keys[N - idx - 1]; 39 | let sub_root = verifier.verify(proof, key, &value)?; 40 | 41 | value = sub_root.clone().into(); 42 | root = Some(sub_root); 43 | } 44 | 45 | Ok(root.expect("MultiVerifier cannot be instantiated with 0 verifiers")) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/enclave/proto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-proto" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Protobuf types defining the Quartz handshake to initialize connection between a smart contract and trusted execution environment. 15 | """ 16 | 17 | 18 | [dependencies] 19 | # external 20 | prost = { workspace = true, features = ["derive"] } 21 | tonic.workspace = true 22 | 23 | [build-dependencies] 24 | tonic-build.workspace = true 25 | -------------------------------------------------------------------------------- /crates/enclave/proto/README.md: -------------------------------------------------------------------------------- 1 | # quartz-proto 2 | 3 | Protobuf types defining the Quartz handshake to initialize connection between a smart contract and trusted execution environment. 4 | -------------------------------------------------------------------------------- /crates/enclave/proto/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .out_dir("src/prost") 4 | .compile_protos(&["proto/quartz.proto"], &["proto"])?; 5 | Ok(()) 6 | } 7 | -------------------------------------------------------------------------------- /crates/enclave/proto/proto/quartz.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package quartz; 4 | 5 | service Core { 6 | rpc Instantiate (InstantiateRequest) returns (InstantiateResponse) {} 7 | rpc SessionCreate (SessionCreateRequest) returns (SessionCreateResponse) {} 8 | rpc SessionSetPubKey (SessionSetPubKeyRequest) returns (SessionSetPubKeyResponse) {} 9 | } 10 | 11 | message InstantiateRequest {} 12 | 13 | message InstantiateResponse { 14 | string message = 1; 15 | } 16 | 17 | message SessionCreateRequest { 18 | string message = 1; 19 | } 20 | 21 | message SessionCreateResponse { 22 | string message = 1; 23 | } 24 | 25 | message SessionSetPubKeyRequest { 26 | string message = 1; 27 | } 28 | 29 | message SessionSetPubKeyResponse { 30 | string message = 1; 31 | } 32 | -------------------------------------------------------------------------------- /crates/enclave/proto/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unwrap_used, unused_qualifications)] 2 | 3 | pub mod quartz { 4 | include!(concat!("prost/", "quartz.rs")); 5 | } 6 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | 3 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-tm-stateless-verifier" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Stateless verification of a tendermint light client proof. 15 | """ 16 | 17 | [dependencies] 18 | # external 19 | displaydoc.workspace = true 20 | 21 | # cosmos 22 | tendermint.workspace = true 23 | tendermint-light-client.workspace = true 24 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/README.md: -------------------------------------------------------------------------------- 1 | # quartz-tm-stateless-verifier 2 | 3 | Stateless verification of a tendermint light client proof. 4 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/src/error.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | 3 | use displaydoc::Display; 4 | use tendermint::{block::Height, Hash}; 5 | use tendermint_light_client::{ 6 | builder::error::Error as TmBuilderError, errors::Error as LightClientError, 7 | }; 8 | 9 | #[derive(Debug, Display)] 10 | pub enum Error { 11 | /// empty trace 12 | EmptyTrace, 13 | /// first block in trace does not match trusted (expected {expected:?}, found {found:?}) 14 | FirstTraceBlockNotTrusted { 15 | expected: (Height, Hash), 16 | found: (Height, Hash), 17 | }, 18 | /// verification failure (`{0}`) 19 | VerificationFailure(Box), 20 | /// failed to build light client (`{0}`) 21 | LightClientBuildFailure(Box), 22 | } 23 | 24 | impl From for Error { 25 | fn from(e: LightClientError) -> Self { 26 | Error::VerificationFailure(Box::new(e)) 27 | } 28 | } 29 | 30 | impl From for Error { 31 | fn from(e: TmBuilderError) -> Self { 32 | Error::LightClientBuildFailure(Box::new(e)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![forbid(unsafe_code)] 3 | #![warn( 4 | clippy::checked_conversions, 5 | clippy::panic, 6 | clippy::panic_in_result_fn, 7 | clippy::unwrap_used, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | rust_2018_idioms, 11 | unused_lifetimes, 12 | unused_import_braces, 13 | unused_qualifications 14 | )] 15 | 16 | extern crate alloc; 17 | 18 | mod error; 19 | mod null_io; 20 | mod provider; 21 | 22 | pub use error::Error; 23 | pub use provider::{make_provider, StatelessProvider}; 24 | -------------------------------------------------------------------------------- /crates/enclave/tm-stateless-verifier/src/null_io.rs: -------------------------------------------------------------------------------- 1 | use tendermint_light_client::{ 2 | components::io::{AtHeight, Io, IoError}, 3 | types::LightBlock, 4 | }; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct NullIo; 8 | 9 | impl Io for NullIo { 10 | fn fetch_light_block(&self, _height: AtHeight) -> Result { 11 | unimplemented!("stateless verification does NOT need access to Io") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /crates/utils/cw-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cw-client" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Rust library for interacting with CosmWasm-enabled blockchains. 15 | Deploy contracts, query them, and execute transactions. 16 | """ 17 | 18 | [lib] 19 | path = "src/lib.rs" 20 | 21 | [dependencies] 22 | anyhow.workspace = true 23 | async-trait.workspace = true 24 | color-eyre.workspace = true 25 | hex.workspace = true 26 | reqwest.workspace = true 27 | serde.workspace = true 28 | serde_json.workspace = true 29 | tonic.workspace = true 30 | 31 | cosmrs = { workspace = true, default-features = false, features = ["cosmwasm"] } 32 | cosmos-sdk-proto = { workspace = true, default-features = false, features = ["grpc", "grpc-transport"] } 33 | tendermint = { workspace = true, default-features = false } 34 | -------------------------------------------------------------------------------- /crates/utils/cw-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use cli::CliClient; 2 | use cosmrs::tendermint::chain::Id; 3 | pub use grpc::GrpcClient; 4 | use hex::ToHex; 5 | use serde::de::DeserializeOwned; 6 | 7 | pub mod cli; 8 | 9 | pub mod grpc; 10 | 11 | #[async_trait::async_trait] 12 | pub trait CwClient { 13 | type Address: AsRef; 14 | type Query: ToString; 15 | type RawQuery: ToHex; 16 | type ChainId: AsRef; 17 | type Error; 18 | 19 | async fn query_smart( 20 | &self, 21 | contract: &Self::Address, 22 | query: Self::Query, 23 | ) -> Result; 24 | 25 | async fn query_raw( 26 | &self, 27 | contract: &Self::Address, 28 | query: Self::RawQuery, 29 | ) -> Result; 30 | 31 | fn query_tx(&self, txhash: &str) -> Result; 32 | 33 | async fn tx_execute( 34 | &self, 35 | contract: &Self::Address, 36 | chain_id: &Id, 37 | gas: u64, 38 | sender: &str, 39 | msg: M, 40 | pay_amount: &str, 41 | ) -> Result; 42 | 43 | fn deploy( 44 | &self, 45 | chain_id: &Id, 46 | sender: &str, // what should this type be 47 | wasm_path: M, 48 | ) -> Result; 49 | 50 | fn init( 51 | &self, 52 | chain_id: &Id, 53 | sender: &str, 54 | code_id: u64, 55 | init_msg: M, 56 | label: &str, 57 | ) -> Result; 58 | 59 | fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error>; 60 | } 61 | -------------------------------------------------------------------------------- /crates/utils/cw-prover/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-cw-prover" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["command-line-utilities", "cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | CLI to generate a merkle proof of a key in the state of a CosmWasm contract. 15 | """ 16 | 17 | [dependencies] 18 | # external 19 | clap.workspace = true 20 | serde_json.workspace = true 21 | tokio.workspace = true 22 | 23 | # cosmos 24 | cosmrs.workspace = true 25 | tendermint.workspace = true 26 | tendermint-rpc.workspace = true 27 | 28 | # quartz 29 | quartz-cw-proof.workspace = true 30 | 31 | [dev-dependencies] 32 | hex.workspace = true 33 | -------------------------------------------------------------------------------- /crates/utils/cw-prover/README.md: -------------------------------------------------------------------------------- 1 | # CosmWasm prover 2 | -------------------------------------------------------------------------------- /crates/utils/print-fmspc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-print-fmspc" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["command-line-utilities", "cryptography::cryptocurrencies", "hardware-support", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | Convenience tool to print the FMSPC (Family-Model-Stepping-Platform-Custom) of the local SGX processor. 15 | """ 16 | 17 | [dependencies] 18 | dcap-qvl = "0.1.0" 19 | hex.workspace = true 20 | serde_json.workspace = true 21 | tokio.workspace = true 22 | -------------------------------------------------------------------------------- /crates/utils/print-fmspc/README.md: -------------------------------------------------------------------------------- 1 | # quartz-print-fmspc 2 | 3 | Convenience tool to print the FMSPC (Family-Model-Stepping-Platform-Custom) of the local SGX processor. 4 | -------------------------------------------------------------------------------- /crates/utils/print-fmspc/src/main.rs: -------------------------------------------------------------------------------- 1 | use dcap_qvl::collateral::get_collateral; 2 | 3 | const DEFAULT_PCCS_URL: &str = "https://localhost:8081/sgx/certification/v4/"; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let pccs_url = std::env::var("PCCS_URL").unwrap_or_else(|_| DEFAULT_PCCS_URL.to_string()); 8 | let quote = { 9 | let quote_hex = std::env::var("QUOTE").expect("QUOTE is not found"); 10 | hex::decode(quote_hex).expect("QUOTE is not valid hex") 11 | }; 12 | 13 | let collateral = get_collateral(&pccs_url, "e, std::time::Duration::from_secs(10)) 14 | .await 15 | .expect("failed to get collateral"); 16 | let tcb_info: serde_json::Value = 17 | serde_json::from_str(&collateral.tcb_info).expect("Retrieved Tcbinfo is not valid JSON"); 18 | 19 | eprintln!("{}", tcb_info["fmspc"]); 20 | } 21 | -------------------------------------------------------------------------------- /crates/utils/tm-prover/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quartz-tm-prover" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | homepage.workspace = true 10 | categories = ["command-line-utilities", "cryptography::cryptocurrencies", "wasm"] 11 | keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"] 12 | readme = "README.md" 13 | description = """ 14 | CLI for generating a Merkle proof of CosmWasm state and accompanying tendermint light client proof (i.e. verification trace) for a given block height and trusted height/hash. Resulting proofs can be verified by quartz-tm-stateless-verifier. 15 | """ 16 | 17 | [[bin]] 18 | name = "main" 19 | path = "src/main.rs" 20 | 21 | [dependencies] 22 | # external 23 | clap.workspace = true 24 | color-eyre.workspace = true 25 | futures.workspace = true 26 | serde.workspace = true 27 | tokio.workspace = true 28 | tracing.workspace = true 29 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 30 | 31 | # cosmos 32 | cosmrs.workspace = true 33 | tendermint.workspace = true 34 | tendermint-rpc.workspace = true 35 | tendermint-light-client.workspace = true 36 | tendermint-light-client-detector.workspace = true 37 | 38 | # quartz 39 | quartz-cw-proof.workspace = true 40 | -------------------------------------------------------------------------------- /crates/utils/tm-prover/README.md: -------------------------------------------------------------------------------- 1 | # The Tendermint light client prover 2 | 3 | Enables stateless light client verification by generating a light client proof (AKA verification trace) for a given 4 | block height and trusted height/hash. 5 | 6 | ## Usage 7 | 8 | ```bash 9 | cargo run -- --chain-id testing \ 10 | --primary "http://127.0.0.1:26657" \ 11 | --witnesses "http://127.0.0.1:26657" \ 12 | --trusted-height 1 \ 13 | --trusted-hash "2EF0E6F9BDDF5DEAA6FCD6492C3DB26D7C62BFFC01B538A958D04376E0B67185" \ 14 | --contract-address "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" \ 15 | --storage-key "quartz_session" \ 16 | --trace-file light-client-proof.json 17 | ``` 18 | -------------------------------------------------------------------------------- /crates/utils/tm-prover/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod prover; 3 | -------------------------------------------------------------------------------- /crates/utils/tm-prover/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use color_eyre::eyre::Result; 3 | use quartz_tm_prover::{config::Config, prover::prove}; 4 | use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | color_eyre::install()?; 9 | 10 | let args = Config::parse(); 11 | 12 | let env_filter = EnvFilter::builder() 13 | .with_default_directive(args.verbose.to_level_filter().into()) 14 | .from_env_lossy(); 15 | 16 | tracing_subscriber::fmt() 17 | .with_target(false) 18 | .with_env_filter(env_filter) 19 | .finish() 20 | .init(); 21 | 22 | let proof = prove(args).await?; 23 | println!("{:?}", proof); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker Images 2 | 3 | | Image | Description | 4 | |-------|-------------| 5 | | [Single node wasmd testnet](./wasmd/) | A single node network with a basic wasmd chain setup. Useful for basic, quick testing of your contracts. | 6 | | [Single node neutron testnet](./neutrond/) | A single node neutron network. Useful for testing your contracts on a more advanced cosmwasm chain, with more up to date dependencies. | 7 | | [SGX enclave](./enclave-sgx/) | A base image for a Quartz enclave to run on an Intel SGX-based machine | 8 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | name: "transfers" 2 | 3 | volumes: 4 | data: 5 | 6 | services: 7 | node: 8 | healthcheck: 9 | test: 10 | [ 11 | "CMD", 12 | "curl", 13 | "-f", 14 | "http://127.0.0.1:1317/cosmos/base/tendermint/v1beta1/blocks/1", 15 | ] 16 | image: juanenrisley/neutrond:4.2.4 17 | volumes: 18 | - data:/root/.neutrond 19 | ports: 20 | - 1317:1317 21 | - 9090:9090 22 | - 26656:26656 23 | - 26657:26657 24 | enclave: 25 | container_name: enclave 26 | environment: 27 | QUARTZ_GRPC_URL: http://node:9090 28 | QUARTZ_NODE_URL: http://node:26657 29 | QUARTZ_WS_URL: ws://node:26657/websocket 30 | ADMIN_SK: 07b291dca4ead76392945ea0a8c35b2d506617d36337788c9043c8fc992213e1 31 | build: 32 | context: ".." 33 | dockerfile: "./crates/cli/Dockerfile" 34 | depends_on: 35 | node: 36 | condition: service_healthy 37 | volumes: 38 | - data:/root/.neutrond 39 | ports: 40 | - 11090:11090 41 | -------------------------------------------------------------------------------- /docker/neutrond/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !data 3 | -------------------------------------------------------------------------------- /docker/neutrond/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:focal-1.2.0 2 | 3 | COPY data /root/ 4 | RUN mv /root/neutrond /usr/local/bin 5 | 6 | CMD bash /root/entrypoint.sh 7 | -------------------------------------------------------------------------------- /docker/neutrond/Makefile: -------------------------------------------------------------------------------- 1 | # the ./accounts/ folder. We assume that the mnemonic for each account is the 2 | # last line of each of the text files. 3 | import-local-accounts: 4 | tail -n 1 data/accounts/admin.txt | neutrond keys add admin --recover --keyring-backend=test 5 | tail -n 1 data/accounts/alice.txt | neutrond keys add alice --recover --keyring-backend=test 6 | tail -n 1 data/accounts/bob.txt | neutrond keys add bob --recover --keyring-backend=test 7 | tail -n 1 data/accounts/charlie.txt | neutrond keys add charlie --recover --keyring-backend=test 8 | .PHONY: import-accounts 9 | -------------------------------------------------------------------------------- /docker/neutrond/README.md: -------------------------------------------------------------------------------- 1 | # Quartz Neutrond image 2 | 3 | This folder contains a `Dockerfile` that helps build a single-node [neutrond] 4 | for use in testing your Quartz application. 5 | 6 | It facilitates the creation of a Docker image with 4 accounts pre-loaded, each 7 | having a small amount of `untrn` preloaded from genesis for experimentation. 8 | 9 | - `admin` 10 | - `alice` 11 | - `bob` 12 | - `charlie` 13 | 14 | These accounts' details are stored in clear text in the [data/accounts](./data/accounts/) 15 | folder. 16 | 17 | **Note: this image is _NOT_ intended to be used in production.** 18 | 19 | ## Updating neutrond binary 20 | 21 | We are using a special neutrond binary that allows to run the node in dev mode to be able to work with quartz correctly. To build this binary you need to follow next steps: 22 | 23 | ```bash 24 | # Clone the target neutrond version you want to build: 25 | git clone --depth 1 --branch v4.2.4 https://github.com/neutron-org/neutron.git /neutron && cd ./neutron 26 | 27 | # Build the binary using special build flag: 28 | make build-static-linux-amd64 BUILD_TAGS=skip_ccv_msg_filter 29 | 30 | # It will generate the binary in `neutron/build/neutrond-linux-amd64`, copy that file: 31 | mv ./build/neutrond-linux-amd64 {PATH_CYCLES_POCKET}/docker/neutrond/data/neutrond 32 | 33 | # And now you can rebuild the docker image with new binary 34 | 35 | ``` 36 | 37 | ## Importing the account keys 38 | 39 | As previously mentioned, the [data/accounts](./data/accounts/) folder contains all of 40 | the necessary material to construct the public/private keypairs of the accounts. 41 | 42 | A convenient helper target is provided in [`/Makefile`](./Makefile) to facilitate 43 | importing of these accounts into a local `neutrond` configuration (i.e. on your 44 | host machine, outside of the Docker container). This will allow you to transact 45 | on behalf of any of those accounts from outside of the Docker container. 46 | 47 | **NB**: For this to work, you will need the same version of `neutrond` installed on 48 | your local machine as what is built into the `neutrond` Docker image. 49 | 50 | ```bash 51 | make import-local-accounts 52 | ``` 53 | 54 | To check that the accounts have been imported correctly, on your host machine 55 | run: 56 | 57 | ```bash 58 | # List all keys available in your local neutrond configuration 59 | neutrond keys list --keyring-backend=test 60 | ``` 61 | -------------------------------------------------------------------------------- /docker/neutrond/data/accounts/admin.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: neutron1zmr7dfc325907tvj8jl2p2p4cx84clk5aflstl 3 | name: admin 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ak3alwik1L65ujlKbgQP//JL3LUlJmvA0zn8rt4eeoHG"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | garage advice weekend this dose mango sign horse tool torch mosquito repeat sentence valid scheme pull punch need prosper build actor say cancel allow 12 | -------------------------------------------------------------------------------- /docker/neutrond/data/accounts/alice.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: neutron152zj7nyksf7dgrt4tpcf64t92k4ar9c2c97wxz 3 | name: alice 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A9jd2NZ40ZkD0TqBMmco6ejP1EzR1AnYIqSNm5m/Y997"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | run snack expand version flag foil used session name lift mouse repeat bunker pencil orchard lens kitchen prevent emerge alien outdoor else cat brass 12 | -------------------------------------------------------------------------------- /docker/neutrond/data/accounts/bob.txt: -------------------------------------------------------------------------------- 1 | override the existing name val1 [y/N]: 2 | - address: neutron1t4jlep6w6qxzwf58x49uya74x4tfjx4vd7v9a7 3 | name: bob 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Are6QBWqMGG3srEdzWj6ulFzX2J9mU5V5BUGDWpPZvzn"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | rare tobacco crisp auction shrug turkey transfer little giggle giraffe live stereo soft arctic raw page october river armor home champion check virus trap 12 | -------------------------------------------------------------------------------- /docker/neutrond/data/accounts/charlie.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: neutron1efkh8rpul8v6km98cuaejz0a5zczvvvz4ps82r 3 | name: charlie 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A0owhBMbRQ2jwJ7dKqwRA2fFyqSMpRmI2aefEdBIT/td"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | photo mobile lady place outdoor camp fruit mouse spell feel service oval draw essay expect tuition fade drip riot manage aunt idle weather clap 12 | -------------------------------------------------------------------------------- /docker/neutrond/data/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Clean previous keys & reset node 5 | rm -rf /root/.neutrond/keyring-test &> /dev/null 6 | neutrond tendermint unsafe-reset-all 7 | 8 | # Init configuration files 9 | neutrond init test --chain-id testing --overwrite 10 | 11 | # Modify default configurations 12 | sed -i 's/keyring-backend = "os"/keyring-backend = "test"/g' /root/.neutrond/config/client.toml 13 | sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \[\"*\"\]/g' /root/.neutrond/config/config.toml 14 | sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.0025untrn"/g' /root/.neutrond/config/app.toml 15 | sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' /root/.neutrond/config/app.toml 16 | sed -i 's/enable = false/enable = true/g' /root/.neutrond/config/app.toml 17 | sed -i 's/swagger = false/swagger = true/g' /root/.neutrond/config/app.toml 18 | 19 | # Changing pruining and empty blocks so our node doesn't grow and cause us to redeploy too often" 20 | sed -i 's/pruning = "default"/pruning = "everything"/g' /root/.neutrond/config/app.toml 21 | sed -i 's/create_empty_blocks = true/create_empty_blocks = false/g' /root/.neutrond/config/config.toml 22 | 23 | # feemarket parameters in genesis.json 24 | sed -i 's/"min_base_gas_price": "[^"]*"/"min_base_gas_price": "0.0025"/g' /root/.neutrond/config/genesis.json 25 | sed -i 's/"fee_denom": "[^"]*"/"fee_denom": "untrn"/g' /root/.neutrond/config/genesis.json 26 | sed -i 's/"base_gas_price": "[^"]*"/"base_gas_price": "0.0025"/g' /root/.neutrond/config/genesis.json 27 | 28 | # Import all test accounts 29 | for filename in /root/accounts/*.txt; do 30 | tail -n 1 "$filename" | neutrond keys add "$(basename "$filename" .txt)" --no-backup --recover --keyring-backend=test 31 | 32 | neutrond add-genesis-account "$(neutrond keys show $(basename "$filename" .txt) -a)" 12000000000000untrn 33 | done 34 | 35 | # Enable as a single node consumer 36 | neutrond add-consumer-section 37 | 38 | # Start node 39 | neutrond start --trace --rpc.laddr="tcp://0.0.0.0:26657" --api.address="tcp://0.0.0.0:1317" --grpc.address="0.0.0.0:9090" 40 | -------------------------------------------------------------------------------- /docker/wasmd/Makefile: -------------------------------------------------------------------------------- 1 | WASMD_VERSION?=v0.44.0 2 | ORG?=informaldev 3 | IMAGE?=$(ORG)/wasmd 4 | .DEFAULT_GOAL := build 5 | 6 | build: 7 | docker build \ 8 | --build-arg="WASMD_VERSION=$(WASMD_VERSION)" \ 9 | -t $(IMAGE):$(WASMD_VERSION) \ 10 | . 11 | .PHONY: build 12 | 13 | run: build 14 | docker run --rm -it \ 15 | -p 26657:26657 -p 26656:26656 -p 1317:1317 -p 9090:9090 \ 16 | --mount type=volume,source=wasmd_data,target=/root \ 17 | --name wasmd \ 18 | $(ORG)/wasmd:$(WASMD_VERSION) 19 | .PHONY: run 20 | 21 | # The create-accounts and delete-accounts commands are exclusively for local use 22 | # to refresh the accounts in ./accounts/ 23 | # 24 | # Both targets require that the correct version of wasmd be installed locally. 25 | create-local-accounts: 26 | mkdir -p ./accounts 27 | wasmd keys add admin > ./accounts/admin.txt 2>&1 28 | wasmd keys add alice > ./accounts/alice.txt 2>&1 29 | wasmd keys add bob > ./accounts/bob.txt 2>&1 30 | wasmd keys add charlie > ./accounts/charlie.txt 2>&1 31 | .PHONY: create-accounts 32 | 33 | delete-local-accounts: 34 | wasmd keys delete -y admin 35 | wasmd keys delete -y alice 36 | wasmd keys delete -y bob 37 | wasmd keys delete -y charlie 38 | .PHONY: delete-accounts 39 | 40 | # Imports the accounts for admin, alice, bob and charlie from the text files in 41 | # the ./accounts/ folder. We assume that the mnemonic for each account is the 42 | # last line of each of the text files. 43 | import-local-accounts: 44 | tail -n 1 accounts/admin.txt | wasmd keys add admin --recover --keyring-backend=test 45 | tail -n 1 accounts/alice.txt | wasmd keys add alice --recover --keyring-backend=test 46 | tail -n 1 accounts/bob.txt | wasmd keys add bob --recover --keyring-backend=test 47 | tail -n 1 accounts/charlie.txt | wasmd keys add charlie --recover --keyring-backend=test 48 | .PHONY: import-accounts 49 | -------------------------------------------------------------------------------- /docker/wasmd/accounts/admin.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: wasm1mkrm9m8g0dzv5z73xg8yzlj6srqc72qru5xfv3 3 | name: admin 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ax3A4I9zO4eI9+3YuU4s2wAy7vQZANa7SgdCjvgkE592"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | scrap toss pottery food asset question utility uncle napkin glare sketch poet entire column comfort traffic hurdle person annual quick party crumble slab tissue 12 | -------------------------------------------------------------------------------- /docker/wasmd/accounts/alice.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: wasm19azg82cx3qx88gar8nl08rz7x0p27amtmadfep 3 | name: alice 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AjTyNLPEA3jD6+BUogvEowwjKi9XjVlJ09RDnc4mhlcR"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | grain shoe diamond mix chunk turn odor across reduce room smart napkin scale ghost emotion stove mistake someone account snow country rail boy rescue 12 | -------------------------------------------------------------------------------- /docker/wasmd/accounts/bob.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: wasm1adcnk7lt6qst7p5d0g5607e28k77um7nxwxuqy 3 | name: bob 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A7IHJ0ihzXP/pQqhCQxH7+qsMEvarKWaVsYqzPeblAn+"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | inmate surface cement any mule sweet what hamster rent ridge series equip equal supply plunge mango mystery chase other economy build pool coyote enter 12 | -------------------------------------------------------------------------------- /docker/wasmd/accounts/charlie.txt: -------------------------------------------------------------------------------- 1 | 2 | - address: wasm1jn34x50hy3my0a2mxwcx8fttgfxu2n5gpvu0ty 3 | name: charlie 4 | pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A9LnFBufvSFCF+aRd3eIB9sSLcguFHOsGJZsaN9D/zx7"}' 5 | type: local 6 | 7 | 8 | **Important** write this mnemonic phrase in a safe place. 9 | It is the only way to recover your account if you ever forget your password. 10 | 11 | wrestle garage wrist bread woman depart service butter sorry soup tunnel gorilla absurd mansion obtain since security hair okay sense fly keep door inject 12 | -------------------------------------------------------------------------------- /docs/arch/README.md: -------------------------------------------------------------------------------- 1 | # Architecture decision records (ADRs) 2 | -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | Quartz is being actively developed by Informal Systems as the core privacy 4 | framework powering the Cycles Protocol. It is designed to be useful more 5 | generally for providing private computation to smart contract platforms. 6 | 7 | - ZKPs 8 | - ORAM 9 | - Forward Secrecy (Key Rotation) 10 | - Managing State 11 | - Multi-Solvers 12 | - Solver Decentralization and Fault Tolerance 13 | - Multi-Verifiers of TEEs and ZKPs (defense-in-depth) 14 | -------------------------------------------------------------------------------- /docs/spec/README.md: -------------------------------------------------------------------------------- 1 | # Specifications 2 | -------------------------------------------------------------------------------- /examples/pingpong/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "target" 3 | -------------------------------------------------------------------------------- /examples/pingpong/README.md: -------------------------------------------------------------------------------- 1 | # Ping Pong 2 | 3 | This is a minimal Quartz demo app to serve as a scaffold to learn and build off of. 4 | 5 | It allows users to submit a message encrypted to the enclave's pubkey, 6 | and receive a response from the enclave encrypted to their own pubkey. 7 | 15 | 16 | # Step by Step 17 | 18 | 1) Run 19 | 20 | ``` 21 | quartz --mock-sgx dev --unsafe-trust-latest --contract-manifest contracts/Cargo.toml 22 | ``` 23 | 24 | 2. Copy the contract address from the output and paste it onto line 46 of the `send_message.rs` script. 25 | 3. Copy the pub key from the handshake and paste it onto line 18 of `send_message.rs`. 26 | 27 | The pubkey in the `Ping` struct will be the user's pubkey, which you can leave untouched. 28 | 29 | 4. Next, send this transaction to "Ping" the enclave. 30 | ``` 31 | cargo run --bin send_message 32 | ``` 33 | 34 | 5. Watch the logs from `quartz dev`. You should see the decrypted message printed. 35 | 36 | Now let's find the enclave's response. 37 | 38 | 6. Query the contract for its messages 39 | ``` 40 | neutrond query wasm contract-state smart $CONTRACT '{ 41 | "get_all_messages": {} 42 | }' -o json 43 | ``` 44 | 45 | 7. Copy the value from, what should be, the only entry in the map. 46 | 47 | 8. Go to `send_message.rs` and paste the hex value onto line 67. Run `decrypt_enclave_response` to see the decrypted output. -------------------------------------------------------------------------------- /examples/pingpong/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | description = "An functioning example of a quartz app" 3 | 4 | ignore = [ 5 | "target/", 6 | "contracts/target", 7 | "contracts/schema", 8 | "enclave/target", 9 | "enclave/src/prost/*", 10 | "frontend/package-lock.json", 11 | ] -------------------------------------------------------------------------------- /examples/pingpong/contracts/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --target wasm32-unknown-unknown --release --lib" 3 | wasm-debug = "build --target wasm32-unknown-unknown --lib" 4 | schema = "run schema" 5 | unit-test = "test --lib" 6 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "ping-pong-contract" 5 | version = "0.2.0" 6 | edition = "2021" 7 | authors = ["Informal Systems "] 8 | exclude = ["contract.wasm", "hash.txt"] 9 | 10 | [[bin]] 11 | name = "schema" 12 | path = "bin/schema.rs" 13 | 14 | [lib] 15 | crate-type = ["cdylib", "rlib"] 16 | 17 | [profile.release] 18 | opt-level = "z" 19 | debug = false 20 | rpath = false 21 | lto = true 22 | debug-assertions = false 23 | codegen-units = 1 24 | panic = 'abort' 25 | incremental = false 26 | overflow-checks = true 27 | 28 | [features] 29 | mock-sgx = ["quartz-contract-core/mock-sgx"] 30 | library = [] 31 | 32 | [dependencies] 33 | # external 34 | serde_json = { version = "1.0.122", default-features = false } 35 | thiserror = { version = "1.0.63" } 36 | 37 | # cosmwasm 38 | cosmwasm-std = { version = "2.1.1", default-features = false, features = [ 39 | "abort", "iterator" 40 | ] } 41 | cosmwasm-schema = { version = "2.1.1", default-features = false } 42 | cw-storage-plus = { version = "2.0.0", default-features = false, features = ["iterator"] } 43 | cw-utils = { version = "2.0.0", default-features = false } 44 | 45 | # quartz 46 | quartz-contract-core = { path = "../../../crates/contracts/core" } 47 | quartz-contract-core-derive = { path = "../../../crates/contracts/core/derive" } 48 | 49 | # patch indirect deps 50 | getrandom = { version = "0.2.15", features = ["js"] } 51 | 52 | [dev-dependencies] 53 | cw-multi-test = { version = "2.1.0", default-features = false } 54 | serde_json = "1.0.122" 55 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use ping_pong_contract::msg::{ExecuteMsg, InstantiateMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use quartz_contract_core::error::Error as QuartzError; 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum ContractError { 7 | #[error("{0}")] 8 | Std(#[from] StdError), 9 | 10 | #[error("{0}")] 11 | Quartz(#[from] QuartzError), 12 | } 13 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![deny( 2 | // warnings, 3 | // trivial_casts, 4 | // trivial_numeric_casts, 5 | // unused_import_braces, 6 | // unused_qualifications 7 | // )] 8 | // #![forbid(unsafe_code)] 9 | 10 | extern crate cosmwasm_std; 11 | 12 | pub mod contract; 13 | pub mod error; 14 | pub mod msg; 15 | pub mod state; 16 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use quartz_contract_core::{ 3 | msg::execute::attested::{RawAttested, RawDefaultAttestation, RawNoop}, 4 | prelude::*, 5 | }; 6 | 7 | pub type AttestedMsg = RawAttested, RA>; 8 | 9 | #[cw_serde] 10 | pub struct InstantiateMsg { 11 | pub quartz: QuartzInstantiateMsg, 12 | } 13 | 14 | #[cw_serde] 15 | #[allow(clippy::large_enum_variant)] 16 | pub enum ExecuteMsg { 17 | // Quartz initialization 18 | Quartz(QuartzExecuteMsg), 19 | // User message 20 | Ping(execute::Ping), 21 | // Enclave message 22 | Pong(AttestedMsg), 23 | } 24 | 25 | pub mod execute { 26 | use cosmwasm_schema::cw_serde; 27 | use cosmwasm_std::HexBinary; 28 | use quartz_contract_core_derive::UserData; 29 | 30 | #[cw_serde] 31 | pub struct Ping { 32 | pub pubkey: HexBinary, 33 | pub message: HexBinary, 34 | } 35 | 36 | #[derive(UserData)] 37 | #[cw_serde] 38 | pub struct Pong { 39 | pub pubkey: HexBinary, 40 | pub response: HexBinary, 41 | } 42 | } 43 | 44 | #[cw_serde] 45 | pub enum QueryMsg { 46 | GetAllMessages {}, 47 | } 48 | -------------------------------------------------------------------------------- /examples/pingpong/contracts/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::HexBinary; 2 | use cw_storage_plus::Map; 3 | 4 | pub const PINGS_KEY: &str = "pings"; 5 | 6 | // Maps pubkeys (String representation of HexBinary) to messages (HexBinary representaton of encrypted data) 7 | // The message that a pubkey maps to is encrypted either to that pubkey or the enclave's pubkey 8 | pub const PINGS: Map = Map::new(PINGS_KEY); 9 | -------------------------------------------------------------------------------- /examples/pingpong/enclave/README.md: -------------------------------------------------------------------------------- 1 | ## Transfers app enclave 2 | -------------------------------------------------------------------------------- /examples/pingpong/enclave/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .out_dir("src/prost") 4 | .compile_protos(&["proto/pingpong.proto"], &["proto"])?; 5 | Ok(()) 6 | } 7 | -------------------------------------------------------------------------------- /examples/pingpong/enclave/proto/pingpong.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pingpong; 4 | 5 | service PingPong { 6 | rpc Run (PingRequest) returns (PongResponse) {} 7 | } 8 | 9 | message PingRequest { 10 | string message = 1; 11 | } 12 | 13 | message PongResponse { 14 | string message = 1; 15 | } 16 | -------------------------------------------------------------------------------- /examples/pingpong/enclave/src/grpc.rs: -------------------------------------------------------------------------------- 1 | use quartz_common::enclave::{handler::Handler, DefaultSharedEnclave}; 2 | use tonic::{Request, Response, Status}; 3 | 4 | use crate::proto::{ping_pong_server::PingPong, PingRequest, PongResponse}; 5 | 6 | #[tonic::async_trait] 7 | impl PingPong for DefaultSharedEnclave<()> { 8 | async fn run(&self, request: Request) -> Result, Status> { 9 | let response = request.handle(self).await?; 10 | Ok(response.map(|r| PongResponse { 11 | message: serde_json::to_string(&r).unwrap(), 12 | })) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/pingpong/enclave/src/proto.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unwrap_used, unused_qualifications)] 2 | 3 | include!(concat!("prost/", "pingpong.rs")); 4 | -------------------------------------------------------------------------------- /examples/pingpong/quartz.neutron_pion-1.toml: -------------------------------------------------------------------------------- 1 | mock_sgx = false 2 | tx_sender = "val1" 3 | chain_id = "pion-1" 4 | node_url = "https://rpc-falcron.pion-1.ntrn.tech" 5 | ws_url = "wss://rpc-falcron.pion-1.ntrn.tech/websocket" 6 | grpc_url = "https://grpc-falcron.pion-1.ntrn.tech:80" 7 | # node_url = "https://neutron-testnet-rpc.polkachu.com" 8 | # ws_url = "wss://neutron-testnet-rpc.polkachu.com/websocket" 9 | # grpc_url = "https://grpc.baryon.remedy.tm.p2p.org:443" 10 | enclave_rpc_addr = "https://127.0.0.1" 11 | enclave_rpc_port = 11091 12 | trusted_hash = "" 13 | trusted_height = 0 14 | release = true 15 | -------------------------------------------------------------------------------- /examples/pingpong/quartz.toml: -------------------------------------------------------------------------------- 1 | mock_sgx = true 2 | tx_sender = "admin" 3 | chain_id = "test-1" 4 | node_url = "http://127.0.0.1:26657" 5 | ws_url = "ws://127.0.0.1:26657/websocket" 6 | grpc_url = "http://127.0.0.1:9090" 7 | enclave_rpc_addr = "http://127.0.0.1" 8 | enclave_rpc_port = 11090 9 | trusted_hash = "" 10 | trusted_height = 0 11 | release = true -------------------------------------------------------------------------------- /examples/pingpong/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.81.0" 3 | targets = ["wasm32-unknown-unknown"] 4 | -------------------------------------------------------------------------------- /examples/transfers/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "target" 3 | -------------------------------------------------------------------------------- /examples/transfers/README.md: -------------------------------------------------------------------------------- 1 | # Transfers 2 | 3 | This is a simple Quartz demo app, complete with Keplr-based frontend. 4 | It allows users to deposit funds to a contract, 5 | transfer them around privately within the contract's encrypted state, 6 | and withdraw whatever funds they have. 7 | 8 | See the [getting started guide](/docs/getting_started.md) to get it quickly 9 | setup on a local testnet without SGX or on a real testnet using an Azure SGX 10 | node. 11 | 12 | See this [video of an early demo of the app at the Flashbots 13 | TEE-salon](https://www.youtube.com/watch?v=3Tv6k02zvBc&t=2517s). 14 | 15 | -------------------------------------------------------------------------------- /examples/transfers/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | description = "An functioning example of a quartz app" 3 | 4 | ignore = [ 5 | "target/", 6 | "contracts/target", 7 | "contracts/schema", 8 | "enclave/target", 9 | "enclave/src/prost/*", 10 | "frontend/package-lock.json", 11 | ] -------------------------------------------------------------------------------- /examples/transfers/contracts/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --target wasm32-unknown-unknown --release --lib" 3 | wasm-debug = "build --target wasm32-unknown-unknown --lib" 4 | schema = "run schema" 5 | unit-test = "test --lib" 6 | -------------------------------------------------------------------------------- /examples/transfers/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "transfers-contract" 3 | version = "0.2.0" 4 | edition = "2021" 5 | rust-version = "1.75.0" 6 | license = "Apache-2.0" 7 | readme = "README.md" 8 | repository = "https://github.com/informalsystems/cycles-quartz" 9 | homepage = "https://cycles.money" 10 | authors = ["Informal Systems "] 11 | exclude = ["contract.wasm", "hash.txt"] 12 | description = """ 13 | """ 14 | 15 | [[bin]] 16 | name = "schema" 17 | path = "bin/schema.rs" 18 | 19 | [lib] 20 | crate-type = ["cdylib", "rlib"] 21 | 22 | [profile.release] 23 | opt-level = "z" 24 | debug = false 25 | rpath = false 26 | lto = true 27 | debug-assertions = false 28 | codegen-units = 1 29 | panic = 'abort' 30 | incremental = false 31 | overflow-checks = true 32 | 33 | [features] 34 | mock-sgx = ["quartz-contract-core/mock-sgx"] 35 | library = [] 36 | 37 | [dependencies] 38 | # external 39 | thiserror = { version = "1.0.63" } 40 | 41 | # cosmwasm 42 | cosmwasm-std = { version = "2.1.1", default-features = false, features = [ 43 | "abort", 44 | ] } 45 | cosmwasm-schema = { version = "2.1.1", default-features = false } 46 | cw-storage-plus = { version = "2.0.0", default-features = false } 47 | cw-utils = { version = "2.0.0", default-features = false } 48 | 49 | # quartz 50 | quartz-contract-core = { path = "../../../crates/contracts/core" } 51 | quartz-contract-core-derive = { path = "../../../crates/contracts/core/derive" } 52 | 53 | # patch indirect deps 54 | getrandom = { version = "0.2.15", features = ["js"] } 55 | 56 | [dev-dependencies] 57 | cw-multi-test = { version = "2.1.0", default-features = false } 58 | serde_json = "1.0.122" 59 | -------------------------------------------------------------------------------- /examples/transfers/contracts/bin/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use transfers_contract::msg::{ExecuteMsg, InstantiateMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | execute: ExecuteMsg, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/transfers/contracts/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use cw_utils::PaymentError; 3 | use quartz_contract_core::error::Error as QuartzError; 4 | use thiserror::Error; 5 | 6 | #[derive(Error, Debug)] 7 | pub enum ContractError { 8 | #[error("{0}")] 9 | Std(#[from] StdError), 10 | 11 | #[error("{0}")] 12 | Quartz(#[from] QuartzError), 13 | 14 | #[error("Unauthorized")] 15 | Unauthorized, 16 | 17 | #[error("Duplicate entry found")] 18 | DuplicateEntry, 19 | 20 | #[error("Invalid length")] 21 | BadLength, 22 | 23 | #[error("Payment error: {0}")] 24 | CwUtil(PaymentError), 25 | } 26 | 27 | impl From for ContractError { 28 | fn from(e: PaymentError) -> Self { 29 | Self::CwUtil(e) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/transfers/contracts/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny( 2 | warnings, 3 | trivial_casts, 4 | trivial_numeric_casts, 5 | unused_import_braces, 6 | unused_qualifications 7 | )] 8 | #![forbid(unsafe_code)] 9 | 10 | extern crate cosmwasm_std; 11 | 12 | pub mod contract; 13 | pub mod error; 14 | pub mod msg; 15 | pub mod state; 16 | -------------------------------------------------------------------------------- /examples/transfers/contracts/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::HexBinary; 2 | use cw_storage_plus::{Item, Map}; 3 | 4 | use crate::msg::execute::Request; 5 | 6 | pub const REQUESTS_KEY: &str = "requests"; 7 | pub const STATE: Item = Item::new("state"); 8 | pub const REQUESTS: Item> = Item::new(REQUESTS_KEY); 9 | pub const DENOM: Item = Item::new("donation_denom"); 10 | pub const BALANCES: Map<&str, HexBinary> = Map::new("balances"); 11 | -------------------------------------------------------------------------------- /examples/transfers/enclave/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/examples/transfers/enclave/.DS_Store -------------------------------------------------------------------------------- /examples/transfers/enclave/README.md: -------------------------------------------------------------------------------- 1 | ## Transfers app enclave 2 | -------------------------------------------------------------------------------- /examples/transfers/enclave/bin/encrypt.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use cosmwasm_std::{Addr, HexBinary, Uint128}; 4 | use ecies::encrypt; 5 | use k256::ecdsa::VerifyingKey; 6 | use serde::{Deserialize, Serialize}; 7 | use transfers_contract::msg::execute::ClearTextTransferRequestMsg; 8 | 9 | pub type RawCipherText = HexBinary; 10 | 11 | #[derive(Clone, Debug)] 12 | pub struct State { 13 | pub state: BTreeMap, 14 | } 15 | 16 | #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 17 | pub struct RawState { 18 | pub state: BTreeMap, 19 | } 20 | 21 | #[derive(Clone, Debug, Serialize, Deserialize)] 22 | pub struct RawEncryptedState { 23 | pub ciphertext: HexBinary, 24 | } 25 | 26 | impl From for RawState { 27 | fn from(o: State) -> Self { 28 | Self { state: o.state } 29 | } 30 | } 31 | 32 | impl TryFrom for State { 33 | type Error = anyhow::Error; 34 | 35 | fn try_from(o: RawState) -> Result { 36 | Ok(Self { state: o.state }) 37 | } 38 | } 39 | 40 | fn main() { 41 | let msg = ClearTextTransferRequestMsg { 42 | sender: Addr::unchecked("alice"), 43 | receiver: Addr::unchecked("bob"), 44 | amount: Uint128::from(100_u32), 45 | }; 46 | 47 | let decoded: Vec = 48 | hex::decode("03e8d63b96a3b3fa442f0a8f39a580f5e898dab7b86eaa685466e82d79022eedff") 49 | .expect("Decoding failed"); 50 | let sk = VerifyingKey::from_sec1_bytes(&decoded).unwrap(); 51 | 52 | let serialized_state = serde_json::to_string(&msg).expect("infallible serializer"); 53 | let encrypted_state = encrypt(&sk.to_sec1_bytes(), serialized_state.as_bytes()).unwrap(); 54 | 55 | let result: HexBinary = encrypted_state.into(); 56 | 57 | println!("{}", result); 58 | } 59 | -------------------------------------------------------------------------------- /examples/transfers/enclave/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .out_dir("src/prost") 4 | .compile_protos(&["proto/transfers.proto"], &["proto"])?; 5 | Ok(()) 6 | } 7 | -------------------------------------------------------------------------------- /examples/transfers/enclave/proto/transfers.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package transfers; 4 | 5 | service Settlement { 6 | rpc Run (UpdateRequest) returns (UpdateResponse) {} 7 | rpc Query (QueryRequest) returns (QueryResponse) {} 8 | } 9 | 10 | message UpdateRequest { 11 | string message = 1; 12 | } 13 | 14 | message UpdateResponse { 15 | string message = 1; 16 | } 17 | 18 | message QueryRequest { 19 | string message = 1; 20 | } 21 | 22 | message QueryResponse { 23 | string message = 1; 24 | } 25 | -------------------------------------------------------------------------------- /examples/transfers/enclave/src/event.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use anyhow::{anyhow, Error as AnyhowError}; 4 | use cosmrs::AccountId; 5 | use quartz_common::enclave::{ 6 | chain_client::{default::Query, ChainClient}, 7 | handler::Handler, 8 | }; 9 | use tendermint_rpc::event::Event as TmEvent; 10 | 11 | use crate::{ 12 | event::{query::QueryEvent, transfer::TransferEvent}, 13 | request::EnclaveRequest, 14 | }; 15 | 16 | pub mod query; 17 | pub mod transfer; 18 | 19 | #[derive(Clone, Debug)] 20 | pub enum EnclaveEvent { 21 | Transfer(TransferEvent), 22 | Query(QueryEvent), 23 | } 24 | 25 | impl TryFrom for EnclaveEvent { 26 | type Error = AnyhowError; 27 | 28 | fn try_from(value: TmEvent) -> Result { 29 | if let Ok(event) = TransferEvent::try_from(value.clone()) { 30 | Ok(Self::Transfer(event)) 31 | } else if let Ok(event) = QueryEvent::try_from(value) { 32 | Ok(Self::Query(event)) 33 | } else { 34 | Err(anyhow::anyhow!("unsupported event")) 35 | } 36 | } 37 | } 38 | 39 | #[async_trait::async_trait] 40 | impl Handler for EnclaveEvent 41 | where 42 | C: ChainClient, 43 | { 44 | type Error = AnyhowError; 45 | type Response = EnclaveRequest; 46 | 47 | async fn handle(self, ctx: &C) -> Result { 48 | match self { 49 | EnclaveEvent::Transfer(event) => event.handle(ctx).await.map(EnclaveRequest::Update), 50 | EnclaveEvent::Query(event) => event.handle(ctx).await.map(EnclaveRequest::Query), 51 | } 52 | } 53 | } 54 | 55 | fn first_event_with_key<'a>( 56 | events: &'a BTreeMap>, 57 | key: &str, 58 | ) -> Result<&'a String, AnyhowError> { 59 | events 60 | .get(key) 61 | .ok_or_else(|| anyhow!("missing execute._contract_address in events"))? 62 | .first() 63 | .ok_or_else(|| anyhow!("execute._contract_address is empty")) 64 | } 65 | -------------------------------------------------------------------------------- /examples/transfers/enclave/src/grpc.rs: -------------------------------------------------------------------------------- 1 | use quartz_common::enclave::{handler::Handler, DefaultSharedEnclave}; 2 | use tonic::{Request, Response, Status}; 3 | 4 | use crate::proto::{ 5 | settlement_server::Settlement, QueryRequest, QueryResponse, UpdateRequest, UpdateResponse, 6 | }; 7 | 8 | #[tonic::async_trait] 9 | impl Settlement for DefaultSharedEnclave<()> { 10 | async fn run( 11 | &self, 12 | request: Request, 13 | ) -> Result, Status> { 14 | let response = request.handle(self).await?; 15 | Ok(response.map(|r| UpdateResponse { 16 | message: serde_json::to_string(&r).unwrap(), 17 | })) 18 | } 19 | 20 | async fn query( 21 | &self, 22 | request: Request, 23 | ) -> Result, Status> { 24 | let response = request.handle(self).await?; 25 | Ok(response.map(|r| QueryResponse { 26 | message: serde_json::to_string(&r).unwrap(), 27 | })) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/transfers/enclave/src/proto.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unwrap_used, unused_qualifications)] 2 | 3 | include!(concat!("prost/", "transfers.rs")); 4 | -------------------------------------------------------------------------------- /examples/transfers/enclave/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use cosmwasm_std::{Addr, Uint128}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize, Serialize)] 7 | pub struct State { 8 | pub state: BTreeMap, 9 | } 10 | 11 | #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 12 | pub struct Balance { 13 | pub balance: Uint128, 14 | } 15 | -------------------------------------------------------------------------------- /examples/transfers/frontend/.env.example: -------------------------------------------------------------------------------- 1 | # App required variables 2 | NEXT_PUBLIC_TARGET_CHAIN=localNeutron 3 | NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=02a4db2129c629a709351bd7d9e09c49326c580cb2863aec9020dccaaa5984a68f 4 | NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq 5 | 6 | # E2E testing required variables 7 | TEST_BASE_URL=http://127.0.0.1:3000 8 | TEST_KEPLR_EXTENSION_VERSION=0.12.124 9 | TEST_WALLET_MNEMONIC=run snack expand version flag foil used session name lift mouse repeat bunker pencil orchard lens kitchen prevent emerge alien outdoor else cat brass 10 | TEST_SECONDARY_WALLET_MNEMONIC=rare tobacco crisp auction shrug turkey transfer little giggle giraffe live stereo soft arctic raw page october river armor home champion check virus trap 11 | TEST_SECONDARY_WALLET_ADDRESS=neutron1t4jlep6w6qxzwf58x49uya74x4tfjx4vd7v9a7 12 | TEST_WALLET_PASSWORD=;pzPCXB^@92byC 13 | PLAYWRIGHT_HTML_OPEN=never 14 | -------------------------------------------------------------------------------- /examples/transfers/frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next", 4 | "plugin:tailwindcss/recommended", 5 | "plugin:prettier/recommended" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/transfers/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /test-results/ 11 | /playwright-report/ 12 | /blob-report/ 13 | /playwright/.cache/ 14 | **/extensions/* 15 | !**/extensions/.gitkeep 16 | 17 | # next.js 18 | /.next/ 19 | /out/ 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # local env files 34 | .env*.local 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /examples/transfers/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "quoteProps": "consistent", 3 | "singleQuote": true, 4 | "semi": false, 5 | "singleAttributePerLine": true, 6 | "tailwindFunctions": ["twJoin", "twMerge"], 7 | "tabWidth": 2, 8 | "trailingComma": "all" 9 | } 10 | -------------------------------------------------------------------------------- /examples/transfers/frontend/README.md: -------------------------------------------------------------------------------- 1 | # Transfer App 2 | 3 | This is an example frontend that illustrates how to interact with a Quartz app. 4 | 5 | This example offers the ability to: 6 | 7 | - Deposit amounts into a balance 8 | - Withdraw the whole deposit 9 | - Transfer amounts between wallets in a private-preserving way 10 | - Query your encrypted balance 11 | - Switch between Keplr wallets 12 | 13 | ## Requirements 14 | 15 | In order to get started, you will need: 16 | 17 | - [Node.js](https://nodejs.org/) LTS (v20.x) 18 | - `npm` 19 | - A [Keplr](https://www.keplr.app/) Wallet 20 | 21 | ## Development 22 | 23 | Install dependencies: 24 | 25 | ```bash 26 | npm ci 27 | ``` 28 | 29 | The app requires some environment variables to fully work. Be sure to set up those accordingly to your local environment. 30 | 31 | You should start from the template: 32 | 33 | ```bash 34 | cp .env.example .env.local 35 | ``` 36 | 37 | Required environment variables: 38 | 39 | ``` 40 | # Choose target chain configuration 41 | NEXT_PUBLIC_TARGET_CHAIN= 42 | # Enclave public key to encrypt transfers 43 | NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY= 44 | # Target transfers contract 45 | NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS= 46 | ``` 47 | 48 | Run the app: 49 | 50 | ```bash 51 | npm run dev 52 | ``` 53 | 54 | App will be running on http://localhost:3000/ and now everything is up & running 🎉 55 | 56 | ## E2E Testing 57 | 58 | For tests to work, you need to set up the following required environment variables: 59 | 60 | ``` 61 | # Frontend base url 62 | TEST_BASE_URL= 63 | # Keplr browser extension version 64 | TEST_KEPLR_EXTENSION_VERSION= 65 | # Main wallet mnemonic (Use only funded wallets) 66 | TEST_WALLET_MNEMONIC= 67 | # Secondary wallet mnemonic (Use only funded wallets) 68 | TEST_SECONDARY_WALLET_MNEMONIC= 69 | # Secondary wallet address 70 | TEST_SECONDARY_WALLET_ADDRESS= 71 | # Keplr wallet password. It can be whatever 72 | TEST_WALLET_PASSWORD= 73 | ``` 74 | 75 | Run all E2E tests: 76 | 77 | ```bash 78 | npm run test 79 | ``` 80 | 81 | If want to run the tests with the Playwright dedicated interface, run: 82 | 83 | ```bash 84 | npm run test:ui 85 | ``` 86 | -------------------------------------------------------------------------------- /examples/transfers/frontend/env.d.ts: -------------------------------------------------------------------------------- 1 | namespace NodeJS { 2 | interface ProcessEnv { 3 | NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY: string 4 | NEXT_PUBLIC_TARGET_CHAIN: string 5 | NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS: string 6 | TEST_BASE_URL: string 7 | TEST_KEPLR_EXTENSION_VERSION: string 8 | TEST_WALLET_MNEMONIC: string 9 | TEST_SECONDARY_WALLET_MNEMONIC: string 10 | TEST_SECONDARY_WALLET_ADDRESS: string 11 | TEST_WALLET_PASSWORD: string 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/transfers/frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | instrumentationHook: true 5 | }, 6 | } 7 | 8 | export default nextConfig 9 | -------------------------------------------------------------------------------- /examples/transfers/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "transfers-fe", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint .", 10 | "lint:fix": "eslint . --fix", 11 | "test": "playwright test", 12 | "test:ui": "playwright test --ui", 13 | "prepare": "npx playwright install chromium" 14 | }, 15 | "dependencies": { 16 | "eciesjs": "^0.4.7", 17 | "graz": "^0.1.19", 18 | "lodash": "^4.17.21", 19 | "next": "14.2.7", 20 | "notistack": "3.0.1", 21 | "react": "18.3.1", 22 | "react-dom": "18.3.1", 23 | "tailwind-merge": "^2.3.0", 24 | "zustand": "4.5.4" 25 | }, 26 | "devDependencies": { 27 | "@keplr-wallet/types": "0.12.103", 28 | "@playwright/test": "^1.46.1", 29 | "@tailwindcss/forms": "^0.5.7", 30 | "@tailwindcss/typography": "^0.5.13", 31 | "@types/lodash": "^4.17.5", 32 | "@types/node": "^20", 33 | "@types/react": "18.3.4", 34 | "@types/react-dom": "18.3.0", 35 | "@types/unzipper": "^0.10.10", 36 | "dotenv": "16.4.5", 37 | "eslint": "^8", 38 | "eslint-config-next": "^14.2.7", 39 | "eslint-config-prettier": "^9.1.0", 40 | "eslint-plugin-prettier": "^5.2.1", 41 | "eslint-plugin-tailwindcss": "^3.17.4", 42 | "pino-pretty": "^11.2.2", 43 | "postcss": "^8", 44 | "prettier": "^3.3.2", 45 | "tailwindcss": "^3.4.4", 46 | "tiny-invariant": "^1.3.3", 47 | "typescript": "^5", 48 | "unzipper": "^0.12.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/transfers/frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /examples/transfers/frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/informalsystems/quartz/23b067131e67d3ddbcc2e73a328b2b2b1608da90/examples/transfers/frontend/public/favicon.png -------------------------------------------------------------------------------- /examples/transfers/frontend/src/app/(anon)/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useRouter } from 'next/navigation' 4 | import { useSuggestChainAndConnect } from 'graz' 5 | 6 | import chain from '@/config/chain' 7 | import { StyledText } from '@/components/StyledText' 8 | import { useGlobalState } from '@/state/useGlobalState' 9 | 10 | export default function Landing() { 11 | const router = useRouter() 12 | const { suggestAndConnect } = useSuggestChainAndConnect({ 13 | onSuccess: () => router.replace('/set-seed'), 14 | }) 15 | 16 | const connectWallet = () => { 17 | useGlobalState.getState().setLoading(true) 18 | suggestAndConnect({ chainInfo: chain }) 19 | } 20 | 21 | return ( 22 |
23 |

Connect your Keplr wallet to log in

24 | 29 | Connect Keplr 30 | 31 |
32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /examples/transfers/frontend/src/app/(anon)/set-seed/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState } from 'react' 4 | import { useRouter } from 'next/navigation' 5 | 6 | import { EnterSeedModal } from '@/components/EnterSeedModal' 7 | import { generateMnemonic, saveMnemonic } from '@/lib/ephemeralKeypair' 8 | import { StyledText } from '@/components/StyledText' 9 | import { useGlobalState } from '@/state/useGlobalState' 10 | 11 | const mnemonic = generateMnemonic() 12 | 13 | export default function SetSeed() { 14 | const router = useRouter() 15 | const [isModalOpen, setIsModalOpen] = useState(false) 16 | 17 | const acceptPhrase = () => { 18 | useGlobalState.getState().setLoading(true) 19 | saveMnemonic(mnemonic) 20 | router.replace('/dashboard') 21 | } 22 | 23 | return ( 24 |
25 |

26 | This will be your recovery seed phrase for your public/private keys: 27 |

28 | 29 | {mnemonic} 30 | 31 |
32 | 37 | Continue with the autogenerated seed phrase 38 | 39 | setIsModalOpen(true)} 43 | > 44 | I want to enter my own recovery phrase instead 45 | 46 |
47 | setIsModalOpen(false)} 50 | /> 51 |
52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /examples/transfers/frontend/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/transfers/frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Raleway } from 'next/font/google' 3 | import Script from 'next/script' 4 | 5 | import './globals.css' 6 | import Root from '@/components/Root' 7 | 8 | const bodyFont = Raleway({ subsets: ['latin'], variable: '--font-raleway' }) 9 | 10 | export const metadata: Metadata = { 11 | title: 'Cycles: Respect the Graph', 12 | description: 'The Open Clearing Protocol.', 13 | icons: [ 14 | { 15 | rel: 'icon', 16 | type: 'image/png', 17 | sizes: '32x32', 18 | url: '/favicon.png', 19 | }, 20 | ], 21 | openGraph: { 22 | type: 'website', 23 | url: 'https://example.com', 24 | title: 'Cycles: Respect the Graph', 25 | description: 'The Open Clearing Protocol.', 26 | siteName: 'Cycles', 27 | images: [ 28 | { 29 | url: 'http://cycles.money/share-sheet-image.jpg', 30 | }, 31 | ], 32 | }, 33 | twitter: { 34 | card: 'summary_large_image', 35 | site: '@site', 36 | creator: '@creator', 37 | images: 'http://cycles.money/share-sheet-image.jpg', 38 | }, 39 | } 40 | 41 | export default function RootLayout({ 42 | children, 43 | }: Readonly<{ 44 | children: React.ReactNode 45 | }>) { 46 | return ( 47 | 48 | 49 | 54 |