From 651815e00be9f8d82d0d8df94bf908a37b9cfe7e Mon Sep 17 00:00:00 2001 From: wasabrot <28702988+wasabrot@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:17:29 +0100 Subject: [PATCH] Ms1review (#11) mainly documentation and build fix --------- Co-authored-by: dastansam --- .codecov.yml | 10 + .github/workflows/checks.yml | 3 +- .github/workflows/docker-build.yml | 2 +- Dockerfile | 2 +- README.md | 12 +- ...c2ae4d30e44937a09d3489e839edfb-latest.json | 1 - docs/INSTRUCTIONS.md | 193 ++++++++++++------ docs/crypto.md | 62 +++--- docs/guest-hyperfridge.md | 4 +- docs/host.md | 30 +-- docs/milestones.md | 4 +- docs/runtime.md | 32 +-- host/src/main.rs | 40 ++-- methods/guest/src/main.rs | 128 ++++++------ 14 files changed, 285 insertions(+), 238 deletions(-) create mode 100644 .codecov.yml delete mode 100644 data/test/test.xml-Receipt-6bb958072180ccc56d839bb0931c58552dc2ae4d30e44937a09d3489e839edfb-latest.json diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..0834535 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + target: 80% # Target coverage is 80% + + patch: # For each individual patch + default: + target: 80% # Target coverage for patches is also 80% + diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 66371b0..e910748 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v3 - name: Install linux dependencies - run: sudo apt-get install -y clang libssl-dev llvm libudev-dev protobuf-compiler + run: sudo apt-get update && sudo apt-get install -y clang libssl-dev llvm libudev-dev protobuf-compiler - name: Install Rust run: | @@ -60,7 +60,6 @@ jobs: - name: Run clippy run: | - RUST_BACKTRACE=1 RISC0_DEV_MODE=true cargo build RISC0_SKIP_BUILD=true cargo clippy --all-targets - name: Check Build diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 70ea131..6190db6 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -2,7 +2,7 @@ name: Docker Build on: push: - branches: [ main ] + branches: [ main, ms1review ] pull_request: branches: [ main ] diff --git a/Dockerfile b/Dockerfile index 603bfce..76a7f8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,8 @@ COPY Cargo.toml / COPY rust-toolchain.toml / # create directory holding generated Image Id of Computation which will be proved. -WORKDIR /host RUN mkdir -p /host/out +RUN rm -R /data/test/*.json WORKDIR / RUN RUST_BACKTRACE=1 RISC0_DEV_MODE=true cargo build --release diff --git a/README.md b/README.md index dcd7eba..dcd370a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Hyperfridge zkVM component! -Welcome to the Hyperfridge RISC Zero component! The idea of hyperfridge is to create a bidirectional bridge to the TradFi world for blockchain applications, secured by Zero-Knowledge tech. This project is supported by [Web3 Foundation](https://web3.foundation/) and lets smart contracts and blockchain Dapps "look inside" a bank account, for example to react on the arrival of a FIAT payment and allows to send FIAT funds through a bank account. All automated, secure, and privacy-preserving. For more information take a look at our [web3 grant application](https://github.com/w3f/Grants-Program/blob/master/applications/hyperfridge.md). Hyperfridge's vision is to create a ZK-based ledger to provide a trustless interfaces to TradFi so that anyone can "plug-in" their own bank-account into the Web3 world, similar as you can do it today with [Stripe](https://en.wikipedia.org/wiki/Stripe,_Inc.) in the Web2 world, but open-sourced and fully trustless. +Welcome to the Hyperfridge RISC Zero component! The idea of hyperfridge is to create a bidirectional bridge to the TradFi world for blockchain applications, secured by Zero-Knowledge tech. This project is supported by [Web3 Foundation](https://web3.foundation/) and lets smart contracts and blockchain DApps "look inside" a bank account. For example, to react on the arrival of a FIAT payment and allow to send funds from a bank account using on-chain wallet. All automated, secure, and privacy-preserving. For more information take a look at our [web3 grant application](https://github.com/w3f/Grants-Program/blob/master/applications/hyperfridge.md). Hyperfridge's vision is to create a ZK-based ledger to provide a trustless interfaces to TradFi so that anyone can "plug-in" their own bank-account into the Web3 world, similar as you can do it today with [Stripe](https://en.wikipedia.org/wiki/Stripe,_Inc.) in the Web2 world, but open-sourced and fully trustless. This repository consists of three modules - a [host](docs/host.md) and [guest](docs/guest-hyperfridge.md) program and a verifier tool which shows how to check the proofs. Look [our testing guide](docs/INSTRUCTIONS.md) to get an idea how it is used. Check out our [cryptographic overview](docs/crypto.md), [performance benchmarks](docs/runtime.md), also the [hyperfridge whitepaper](https://github.com/element36-io/ocw-ebics/blob/main/docs/hyperfridge-draft.pdf). - ## Quick Start with Risc-Zero Framework As it builds upon Risc-Zero zkVM, make yourself familiar with this framework, otherwise it will be hard to understand what this crate is doing: @@ -19,20 +18,20 @@ As it builds upon Risc-Zero zkVM, make yourself familiar with this framework, ot [`risc0-build`][risc0-build], and [others][crates]. - [excerpt from Risc0 workshop at ZK HACK III][zkhack-iii]. - ## Development Environment - First, make sure [rustup] is installed. The [`rust-toolchain.toml`][rust-toolchain] file will be used by `cargo` to automatically install the correct version. To build and execute a quick integration test, use: +Second, make sure you have installed `risc0` toolchain. Follow instructions from [here][quick-start]. + ```bash RISC0_DEV_MODE=1 cargo test ``` This will create a STARK based on the provided test data. Check out [our testing guide](docs/INSTRUCTIONS.md) to run test and play with test data. Note that `cargo test` will not invoke tests in `methods/guest` due to the architecture of Risc-Zero framework - see testing guide how to run tests for the poofer. -To use this crate with your owen bank data, you will need to run a component, which connects with our banking backend and then prepare the input for the Hyperfridge zkVM component. You can use the [ebics-java-client][ebics-java-client], but any Ebics client will do, as long as you get access to the XML files which are exchanged between your client and the banking server. +To use this crate with your owen bank data, you will need to run a component, which connects with our banking backend and then prepare the input for the Hyperfridge zkVM component. You can use the [ebics-java-client][ebics-java-client], but any EBICS client will do, as long as you get access to the XML files which are exchanged between your client and the banking server. Open documentation in any module by: @@ -103,6 +102,7 @@ project_name [bonsai access]: https://bonsai.xyz/apply [cargo-risczero]: https://docs.rs/cargo-risczero [crates]: https://github.com/risc0/risc0/blob/main/README.md#rust-binaries +[quick-start]: https://dev.risczero.com/api/zkvm/quickstart [dev-docs]: https://dev.risczero.com [dev-mode]: https://dev.risczero.com/api/zkvm/dev-mode [docs.rs]: https://docs.rs/releases/search?query=risc0 @@ -114,4 +114,4 @@ project_name [rust-toolchain]: rust-toolchain.toml [zkvm-overview]: https://dev.risczero.com/zkvm [zkhack-iii]: https://www.youtube.com/watch?v=Yg_BGqj_6lg&list=PLcPzhUaCxlCgig7ofeARMPwQ8vbuD6hC5&index=5 -[ebics-java-client]: https://bonsai.xyz/apply \ No newline at end of file +[ebics-java-client]: https://github.com/element36-io/ebics-java-client \ No newline at end of file diff --git a/data/test/test.xml-Receipt-6bb958072180ccc56d839bb0931c58552dc2ae4d30e44937a09d3489e839edfb-latest.json b/data/test/test.xml-Receipt-6bb958072180ccc56d839bb0931c58552dc2ae4d30e44937a09d3489e839edfb-latest.json deleted file mode 100644 index 7495313..0000000 --- a/data/test/test.xml-Receipt-6bb958072180ccc56d839bb0931c58552dc2ae4d30e44937a09d3489e839edfb-latest.json +++ /dev/null @@ -1 +0,0 @@ -{"inner":"Fake","journal":{"bytes":[232,6,0,0,123,34,104,111,115,116,105,110,102,111,34,58,34,104,111,115,116,58,109,97,105,110,34,44,34,105,98,97,110,34,58,34,67,72,52,51,48,56,51,48,55,48,48,48,50,56,57,53,51,55,51,49,50,34,44,34,112,117,98,95,98,97,110,107,95,112,101,109,34,58,34,45,45,45,45,45,66,69,71,73,78,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,77,73,73,66,73,106,65,78,66,103,107,113,104,107,105,71,57,119,48,66,65,81,69,70,65,65,79,67,65,81,56,65,77,73,73,66,67,103,75,67,65,81,69,65,118,105,122,103,106,47,112,112,75,108,50,122,89,68,52,109,120,115,106,115,92,110,111,43,52,106,105,43,119,120,57,65,77,81,70,112,75,114,100,81,57,65,72,70,81,76,51,52,55,66,89,105,99,110,48,122,118,110,110,111,68,65,119,76,53,68,48,49,50,90,49,69,89,74,43,90,122,49,71,73,116,56,51,108,105,52,92,110,98,66,83,55,113,110,84,57,113,48,104,116,108,54,120,56,112,86,115,122,88,121,105,55,118,65,57,113,79,87,87,73,67,109,112,54,106,112,47,122,79,43,110,86,87,75,69,73,107,68,101,107,75,87,48,117,66,119,98,88,77,115,65,51,92,110,104,43,55,121,65,80,74,97,112,85,119,76,78,65,109,71,50,71,115,88,81,112,49,72,87,79,75,90,107,84,70,100,68,66,71,55,110,74,74,53,115,99,99,48,65,105,119,82,106,66,50,98,116,118,118,78,81,110,71,43,66,71,122,52,92,110,55,97,51,105,50,57,48,74,57,49,70,106,98,103,114,43,48,66,67,50,118,104,105,51,100,72,106,68,68,68,70,119,51,121,47,43,56,73,99,106,97,112,105,55,85,80,104,88,57,72,68,78,117,109,53,108,81,122,119,118,89,69,67,106,92,110,51,75,115,71,55,80,55,86,50,99,51,71,82,81,100,77,65,54,116,52,107,83,117,98,47,100,57,65,71,112,73,53,98,82,112,52,73,122,43,76,97,69,87,68,70,109,52,121,78,48,89,77,75,53,115,108,57,65,110,56,89,80,80,103,92,110,99,119,73,68,65,81,65,66,92,110,45,45,45,45,45,69,78,68,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,34,44,34,112,117,98,95,119,105,116,110,101,115,115,95,112,101,109,34,58,34,45,45,45,45,45,66,69,71,73,78,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,77,73,73,66,73,106,65,78,66,103,107,113,104,107,105,71,57,119,48,66,65,81,69,70,65,65,79,67,65,81,56,65,77,73,73,66,67,103,75,67,65,81,69,65,110,103,110,111,76,101,99,51,81,87,122,72,107,103,71,87,55,85,106,50,92,110,105,50,121,70,112,56,54,75,68,117,75,114,70,85,117,115,54,112,88,72,74,109,67,110,90,73,76,84,65,79,105,75,122,78,67,65,66,53,113,73,68,66,119,97,57,104,53,48,47,79,84,90,54,112,118,49,88,53,109,103,86,77,50,83,92,110,80,78,75,118,90,111,85,114,102,79,85,54,74,103,53,109,49,98,51,71,107,121,76,106,47,51,65,102,100,83,43,110,74,98,106,85,88,70,108,121,77,87,73,105,53,99,50,54,87,118,118,87,50,70,115,113,115,69,111,101,104,65,71,70,92,110,81,112,117,114,90,86,54,81,75,87,83,75,69,107,49,54,84,75,111,73,50,107,99,68,56,115,69,65,85,98,53,84,86,119,120,43,55,68,53,107,122,56,90,103,85,88,48,103,47,75,113,77,43,111,50,107,85,120,66,105,83,75,100,83,92,110,49,112,57,67,68,69,104,119,87,87,101,48,77,82,48,106,97,52,69,104,54,43,112,70,121,73,73,106,86,115,114,121,98,66,57,117,102,66,117,117,66,67,51,49,114,101,100,70,71,90,52,110,66,88,52,51,120,116,115,53,68,111,54,90,92,110,54,51,85,49,108,88,49,53,103,78,105,74,116,86,120,108,100,66,102,75,109,57,111,50,111,102,80,77,120,100,80,117,51,75,88,69,103,55,102,51,90,109,50,110,57,101,65,49,70,120,85,75,117,114,119,97,99,55,97,51,49,86,56,100,92,110,76,119,73,68,65,81,65,66,92,110,45,45,45,45,45,69,78,68,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,34,44,34,112,117,98,95,99,108,105,101,110,116,95,112,101,109,34,58,34,45,45,45,45,45,66,69,71,73,78,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,77,73,73,66,73,106,65,78,66,103,107,113,104,107,105,71,57,119,48,66,65,81,69,70,65,65,79,67,65,81,56,65,77,73,73,66,67,103,75,67,65,81,69,65,118,108,98,116,69,83,43,108,106,67,51,117,100,69,110,101,97,84,121,102,92,110,88,109,76,118,52,108,52,104,119,117,88,83,69,102,67,73,89,85,102,86,90,105,68,72,122,100,101,71,75,56,119,74,50,103,82,83,117,99,66,115,120,114,114,50,78,69,83,100,72,117,73,114,69,80,109,68,104,87,72,110,69,52,68,54,92,110,74,121,54,49,87,117,76,56,81,87,86,117,105,66,84,90,70,116,83,67,103,82,73,121,101,73,57,111,106,78,84,113,113,48,118,109,79,55,87,106,49,89,57,70,89,100,73,90,47,105,78,56,104,57,120,79,99,117,117,81,107,106,97,55,92,110,50,111,84,117,77,47,97,113,71,108,121,99,84,98,74,83,111,105,111,66,107,118,53,85,98,98,99,68,122,52,77,90,48,83,105,57,82,65,87,51,68,43,52,73,87,101,80,102,75,105,101,84,69,101,84,51,72,89,110,66,66,67,104,76,92,110,83,52,112,67,49,115,105,52,52,120,122,57,118,113,74,99,106,55,122,65,79,108,112,103,97,74,43,118,69,104,76,51,102,47,101,52,113,89,114,98,50,51,82,57,75,66,89,52,85,105,54,85,65,49,101,120,77,109,87,80,98,77,115,55,92,110,110,105,52,98,115,57,51,121,121,105,78,116,112,81,108,81,57,115,79,86,54,72,111,80,111,111,75,80,72,73,117,102,107,47,106,108,70,100,79,73,104,66,52,109,49,88,116,69,114,72,73,71,83,82,112,55,66,116,50,78,116,70,97,98,92,110,72,81,73,68,65,81,65,66,92,110,45,45,45,45,45,69,78,68,32,80,85,66,76,73,67,32,75,69,89,45,45,45,45,45,92,110,34,44,34,115,116,109,116,115,34,58,91,123,34,101,108,99,116,114,110,99,95,115,101,113,95,110,98,34,58,34,50,52,55,34,44,34,102,114,95,100,116,95,116,109,34,58,34,50,48,50,51,45,49,49,45,50,57,84,48,48,58,48,48,58,48,48,34,44,34,116,111,95,100,116,95,116,109,34,58,34,50,48,50,51,45,49,49,45,50,57,84,48,48,58,48,48,58,48,48,34,44,34,97,109,116,34,58,34,51,49,55,48,57,46,49,52,34,44,34,99,99,121,34,58,34,67,72,70,34,44,34,99,100,34,58,34,79,80,66,68,34,125,44,123,34,101,108,99,116,114,110,99,95,115,101,113,95,110,98,34,58,34,50,52,56,34,44,34,102,114,95,100,116,95,116,109,34,58,34,50,48,50,51,45,49,49,45,51,48,84,48,48,58,48,48,58,48,48,34,44,34,116,111,95,100,116,95,116,109,34,58,34,50,48,50,51,45,49,49,45,51,48,84,48,48,58,48,48,58,48,48,34,44,34,97,109,116,34,58,34,51,49,55,48,57,46,48,57,34,44,34,99,99,121,34,58,34,67,72,70,34,44,34,99,100,34,58,34,79,80,66,68,34,125,93,125]}} \ No newline at end of file diff --git a/docs/INSTRUCTIONS.md b/docs/INSTRUCTIONS.md index 6d3542a..c781bee 100644 --- a/docs/INSTRUCTIONS.md +++ b/docs/INSTRUCTIONS.md @@ -2,126 +2,93 @@ This document describes in-depth our rust-modules and the zero-knowledge proof, and how to run various tests. + + For better understanding, lets look at roundtrip of the proofing system: -1. Request and retrieval of banking documents with daily statements (Ebics request and response) through an Ebics banking client, e.g. [ebics-java-client]. The client -2. Pre-Processing of the Ebics Response, which is an XML document. Pre-processing is necessary to off-load as much as possible from expensive proof-generation and to keep the proof-code flexible. Pre-processing is done with script 'data/checkResponse.sh' +1. Request and retrieval of banking documents with daily statements (EBICS request and response) through an EBICS banking client, e.g. [ebics-java-client]. The client +2. Pre-Processing of the EBICS Response, which is an XML document. Pre-processing is necessary to off-load as much as possible from expensive proof-generation and to get a small footprint of the proof-code. Pre-processing is done with script 'data/checkResponse.sh' 3. Present data from the previous step and the private key of the client to the prover, and generate proof of computation (a STARK) and produce the [Receipt] which contains balance, currency date and account-number. 4. A generic risc0 based verifier can check the proof, thus the above account data can be trusted. -5. On-chain integration (validation) of the proof-system using the Substrate Off-Chain-Worker. - -**Important note**: [Milestone 1](https://github.com/w3f/Grants-Program/blob/master/applications/hyperfridge.md#milestone-1---risk-zero-zkp-implementation-based-on-static-test-data) only covers work around generating and validating the STARK with the Risk-Zero framework - so "1." is not included yet - this will be done in milestone 2. Starting with milestone 3 integration with substrate gets implemented. - +5. On-chain integration (validation) of the proof-system using the Substrate Off-Chain-Worker. -## Releases and installation - -The binary distribution can be downloaded from [github](https://github.com/element36-io/hyperfridge-r0/releases). Docker containers are on [dockerhub](https://hub.docker.com/repository/docker/e36io/hyperfridge-r0/general). It is crucial to understand the concept of a "sealed" binary. Means, that the (RiscV) binary producing the STARK is pinned by its hash ("Image-ID"). Proofs can only be validated if you know the Image-Id, that is why we included the Image-ID in the releases and docker tags and as a file (IMAGE_ID.hex) in the distributions. +**Important note**: [Milestone 1](https://github.com/w3f/Grants-Program/blob/master/applications/hyperfridge.md#milestone-1---risk-zero-zkp-implementation-based-on-static-test-data) only covers step 2. and 3., generating and validating the STARK with the Risk-Zero framework - other steps will be done in later milestones. Starting with milestone 3 integration with substrate gets implemented. +***Note:*** You may remove `RISC0_DEV_MODE=true` variable to create a real proof, expect the execution time to be several hours to create the STARK. You may add `--verbose` after each command (host or verifier) to see what is going on. Use `RUST_BACKTRACE=1` to debug. -### Preparations +[![codecov](https://codecov.io/gh/element36-io/hyperfridge-r0/graph/badge.svg?token=JNQZL1G2OM)](https://codecov.io/gh/element36-io/hyperfridge-r0) +Remark on code coverage: Module `methods/guest` can not be shown because the Risc-Zero framework compiles to Risc V instruction set. -If you are using the binary distribution make sure you are running a glibc compatible environment and necessary tools are installed to run the scripts for pre-processing the Ebics Response. On debian based systems you may use `apt install -y openssl perl qpdf xxd libxml2-utils` - versions are given only as FYI. +## Test with Docker -```bash -ldd /bin/bash # linux-vdso.so.1 (0x00007ffc33bee000) .... -opennssl version # output, e.g. OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022) -xxd --version # xxd 2021-10-22 by Juergen Weigert et al. -zlib-flate --version # zlib-flate from qpdf version 10.6.3 -xmllint --version # xmllint: using libxml version 20913 -perl --version # 5 Version 34 +Docker containers are on [dockerhub](https://hub.docker.com/repository/docker/e36io/hyperfridge-r0/general). It is crucial to understand the concept of a "sealed" binary. Means, that the (RiscV) binary producing the STARK is pinned by its hash ("Image-ID"). Proofs can only be validated if you know the Image-Id, that is why we included the Image-ID in the releases and docker tags and as a file (IMAGE_ID.hex) in the distributions. -``` +### Preparations for using Docker Using docker, make sure its installed: + ```bash docker --version # output, e.g. Docker version 24.0.7, build afdd53b ``` Label the [hyperfridge container from dockerhub](https://hub.docker.com/r/e36io/hyperfridge-r0/tags) you want to use with a shortcut "fridge" for later usage: + ```bash -docker tag e36io/hyperfridge-r0:latest fridge +docker pull e36io/hyperfridge-r0:latest +docker tag e36io/hyperfridge-r0:latest fridge # no output is given by docker ``` Or test with docker using your local container build: ```bash +# optional, don't do this for first tests unless you know what you are doing docker build . -t fridge ``` -Test your installation by showing the command line help: - -```bash -host --help -# or with docker -docker run fridge host --help -# output should show command line parameters: Usage: host [OPTIONS] [COMMAND] ... -``` - -***Note:*** You may remove `RISC0_DEV_MODE=true` variable to create a real proof, expect the execution time to be several hours to create the STARK. You may add `--verbose` after each command (host or verifier) to see what is going on. Use `RUST_BACKTRACE=1` to debug. - ### Integration tests with provided test data We included all test data which is necessary to run a quick shake-down test to generate and validate a proof in one go. This creates a proof based on test data, prints the JSON-receipt which is the STARK-proof and contains public and committed data. Steps "3." and "4." of the roundtrip are tested in that way. - ```bash -host test -# or with docker: +# The test generates a proof and validates it docker run --env RISC0_DEV_MODE=true fridge host test ``` You may create new keys, additional test data and payload which is described [here](testdata.md). -### Create recipe (STARK proof) +### Create a receipt (STARK proof) -With binaries: +Show help output: ```bash # show help -host prove-camt53 --help - -# create the proof -RISC0_DEV_MODE=true host prove-camt53 \ - --request="../data/test.xml" --bankkey ../data/pub_bank.pem \ - --clientkey ../data/client.pem --witnesskey ../data/pub_witness.pem \ - --clientiban CH4308307000289537312 +docker run fridge host prove-camt53 --help ``` -Using docker: +Create the proof as JSON file, which can be deserialized and verified in Rust: ```bash -# show help -docker run fridge host prove-camt53 --help - # create the proof docker run --env RISC0_DEV_MODE=true fridge host prove-camt53 \ - --request="../data/test/test.xml" --bankkey ../data/pub_bank.pem \ + --request=../data/test/test.xml --bankkey ../data/pub_bank.pem \ --clientkey ../data/client.pem --witnesskey ../data/pub_witness.pem \ --clientiban CH4308307000289537312 ``` -### Check recipe (STARK proof) -With binaries: +### Check Receipt (STARK proof) + +Show help: ```bash # show help -verifier verify --help - -# we need the image id and the receipt -imageid=$(cat IMAGE_ID.hex) -proof=../data/test/test.xml-Reiceipt-test.json - -verifier verify --imageid-hex=$imageid --proof-json=$proof +docker run fridge verifier verify --help ``` -With docker: +Verify a receipt (json-file) and show its contents (public commitments): ```bash -# show help -docker run fridge verifier verify --help - # we need the image id and the receipt imageid=$(docker run fridge cat /app/IMAGE_ID.hex) proof=/data/test/test.xml-Receipt-test.json @@ -130,11 +97,85 @@ proof=/data/test/test.xml-Receipt-test.json docker run --env RISC0_DEV_MODE=true fridge verifier verify --imageid-hex=$imageid --proof-json=$proof ``` +## Test with binary Release and command line -## Tests in rust environment +The binary distribution can be downloaded from [github](https://github.com/element36-io/hyperfridge-r0/releases). It is crucial to understand the concept of a "sealed" binary. Means, that the (RiscV) binary producing the STARK is pinned by its hash ("Image-ID"). Proofs can only be validated if you know the Image-Id, that is why we included the Image-ID in the releases and docker tags and as a file (IMAGE_ID.hex) in the distributions. -We assume you have [installed rust](https://github.com/element36-io/ocw-ebics/blob/main/docs/rust-setup.md) and [risk zero environment](https://dev.risczero.com/api/zkvm/install). +### Preparations for command line and scripts +If you are using the binary distribution make sure you are running a glibc compatible environment and necessary tools are installed to run the scripts for pre-processing the EBICS Response. On debian based systems you may use `apt install -y openssl perl qpdf xxd libxml2-utils` - versions are given only as FYI. + +```bash +ldd /bin/bash # linux-vdso.so.1 (0x00007ffc33bee000) .... +opennssl version # output, e.g. OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022) +xxd --version # xxd 2021-10-22 by Juergen Weigert et al. +zlib-flate --version # zlib-flate from qpdf version 10.6.3 +xmllint --version # xmllint: using libxml version 20913 +perl --version # 5 Version 34 +``` + +Download binary realease from this repo, unzip the realese. Test your installation by showing the command line help: + +```bash +./host --help +# output should show command line parameters: Usage: host [OPTIONS] [COMMAND] ... +``` + +Get Image-ID of the linked guest code: + +```bash +./host show-image-id +# output, e.g.: a54af3e5a903cc5d80f900f12785a337c1872098c2aacf0b7de28d7b8d6c3fe6 +``` + +We included all test data in `data` directory which is necessary to run a quick shake-down test to generate and validate a proof in one go. This creates a proof based on test data, prints the JSON-receipt which is the STARK-proof and contains public and committed data. Steps "3." and "4." of the roundtrip are tested in that way. + +```bash +# The test generates a proof and validates it +RISC0_DEV_MODE=true ./host test +``` + +You may create new keys, additional test data and payload which is described [here](testdata.md). + +### Create dev receipt (STARK proof) + +In the app directory: + +```bash +# show help +./host prove-camt53 --help +``` + +```bash +# create the proof +RISC0_DEV_MODE=true ./host prove-camt53 \ + --request ../data/test/test.xml --bankkey ../data/pub_bank.pem \ + --clientkey ../data/client.pem --witnesskey ../data/pub_witness.pem \ + --clientiban CH4308307000289537312 +``` + +### Check Receipt (STARK proof) + +In the app directory of the binary distribution: + +```bash +# show help +./verifier verify --help +``` + +Verify the proof: + +```bash +# we need the image id and the receipt +imageid=$(cat IMAGE_ID.hex) +proof=../data/test/test.xml-Receipt-$imageid-latest.json +echo verify with $imageid +RISC0_DEV_MODE=true ./verifier verify --imageid-hex=$imageid --proof-json=$proof +``` + +## Tests in Rust environment + +We assume you have [installed rust](https://github.com/element36-io/ocw-ebics/blob/main/docs/rust-setup.md) and [risk zero environment](https://dev.risczero.com/api/zkvm/install). ### Unit tests @@ -173,13 +214,17 @@ RISC0_DEV_MODE=true \ cargo run -- --verbose prove-camt53 \ --request="../data/myrequest-generated/myrequest-generated.xml" --bankkey ../data/pub_bank.pem \ --clientkey ../data/client.pem --witnesskey ../data/pub_witness.pem --clientiban CH4308307000289537312 +``` + +Lets check the output: +```bash # You see receipt in output of the command and serialized in a json file: ls -la ../data/myrequest-generated/*.json cat ../data/myrequest-generated/*.json ``` -Now let's try to create a fake proof - we will point to a wrong public keys where the verification of signatures should fail: +Now let's try to create a fake proof - we will use wrong public keys where the verification of signatures should fail: ```bash # note that we use witness key for bank: --bankkey ../data/pub_witness.pem @@ -191,7 +236,11 @@ cargo run -- --verbose prove-camt53 \ # panics, output: # verify bank signature # ---> error Verification +``` + +Wrong witness: +```bash # note that we use bank key for witness: --witnesskey ../data/pub_bank.pem RISC0_DEV_MODE=true \ cargo run -- --verbose prove-camt53 \ @@ -203,6 +252,18 @@ cargo run -- --verbose prove-camt53 \ ``` +### Create real receipt with CUDA hardware acceleration + +Note that `RISC0_DEV_MODE=false` and add feature "cuda" to `host/Cargo.toml`. + +```bash +cd ../host +RISC0_DEV_MODE=false \ +cargo run -f cuda -- --verbose prove-camt53 \ + --request="../data/myrequest-generated/myrequest-generated.xml" --bankkey ../data/pub_bank.pem \ + --clientkey ../data/client.pem --witnesskey ../data/pub_witness.pem --clientiban CH4308307000289537312 +``` + Use verifier to check the receipt, move to `verifier` directory: ```bash @@ -217,12 +278,8 @@ proof=$(find ../data/myrequest-generated/ -type f -name "*.json" | head -n 1) RISC0_DEV_MODE=true \ cargo run -- --verbose verify \ --imageid-hex=$imageid --proof-json=$proof - ``` ### Notes on provided test data The test data was taken from the productive systems (Hypo Lenzburg), decrypted and encrypted again with generated keys as it can be seen in scripts `data/createTestResponse.sh` and `data/checkResponse.sh`. A productive sample has been added to `data/test/test.xml (prod).zip`, the payload of test data remained unchanged from the productive system. - -[ebics-java-client2]: [references.json#ebics-java-client] -[r0-dev-mode]: [references.json#r0-dev-mode] \ No newline at end of file diff --git a/docs/crypto.md b/docs/crypto.md index da1abda..67560a8 100644 --- a/docs/crypto.md +++ b/docs/crypto.md @@ -19,11 +19,12 @@ ### The witness role -The EBICS Specification does not enforce encryption of the payload (bank statements).Therefore the client is able to change for example balances or transactions and create a valid proof the the data. We use the concept of a witness (could also be named as signing proxy), which interacts with the bank on the clients behalf without knowing the private key of the client. The witness uses an HSM (Hardware Security Module, e.g. Google HSM) secured by a token to sign messages for the client and exchange data with the bank. -Ebics standard plans the signing of the payload, which would make the witness superluss. The schema-definitions already contain a placeholder, but they are deactivated using 'maxOccurs=0' which leads to a failure of schema validation if the signature of the payload is added to the Ebics-Response. +The EBICS Specification does not enforce encryption of the payload (bank statements). Therefore the client is able to change for example balances and create a valid proof the data. We use the concept of a witness (could also be named as signing proxy), which interacts with the bank on the clients behalf without knowing the private key of the client. The witness uses an HSM (Hardware Security Module, e.g. Google HSM) secured by a token to sign messages for the client and exchange data with the bank. -Snippet of 'ebics_orders_H005.xsd', the schema-specification: +EBICS standard plans the signing of the payload, which would make the witness superluss. The schema-definitions already contain a placeholder, but they are deactivated using 'maxOccurs=0' which leads to a failure of schema validation if the signature of the payload is added to the EBICS-Response. + +Snippet of 'ebics_orders_H005.xsd', which is part of the EBICS the schema-specification for their XML documents which are exchanged between Client and Bank: ```xml ... @@ -31,7 +32,7 @@ Snippet of 'ebics_orders_H005.xsd', the schema-specification: ### EBICS Encryption and Decryption Process -All messages between Client and Bank are singed XML documents. The payload is encrypted with a symetric key, whihc is shared with the client by encrypting it with the clients public key. +All messages between Client and Bank are singed XML documents. The payload is encrypted with a symetric key, which is encrypting it with the Client's public key. ### Process Description when downloading payload data @@ -39,40 +40,40 @@ All messages between Client and Bank are singed XML documents. The payload is en A symmetric key ${SymKey}$ is generated by the Bank to encrypt the payload. 1. **Payload Encryption**: The payload is encrypted using a newly created symmetric key: - $`{Encrypt}_{SymKey}({Payload}) \rightarrow {Payload}_{enc}`$ + ${Encrypt}_{SymKey}({Payload}) \rightarrow {Payload}_{enc}$ 1. **Symmetric Key Encryption**: The symmetric key (SymKey) is then encrypted using the Client's public key, $C_{{pub}}$. This ensures that only the holder of the corresponding private key can decrypt and use the symmetric key. - Encrypted Symmetric Key: $`{Encrypt}_{C_{pub}}({SymKey}) \rightarrow {SymKey}_{enc}`$ -1. **Create Ebics Respose**: - After a client sends an EbicsRequest, the Bank ready to sign and send an EbicsResponse which contains : ${ebicsResponse} = {Payload}_{enc} \, \| \, {EncryptedSessionKey} \, \| \, {XMLSignature}$ + Encrypted Symmetric Key: ${Encrypt}_{C_{pub}}({SymKey}) \rightarrow {SymKey}_{enc}$ +1. **Create EBICS Respose**: + After a client sends an EbicsRequest, the Bank ready to sign and send an EbicsResponse which contains ($\|$ denotes concatentation): ${ebicsResponse} = {Payload}_{enc} \| {EncryptedSessionKey} \| {XMLSignature}$ ### Process Description with Witness and HSM -To work on the Clients behalf, the witness needs to sign messages for the banking backend, for example it needs to create a singed EbicsRequest message, in order to get the EbicsResponse message which contains the payload. +To work on the Clients behalf, the Witness needs to sign messages for the banking backend, for example it needs to create a singed EbicsRequest message, in order to get the EbicsResponse message which contains the payload containing balance and transactions: To sign messages for the bank it uses : -4. **Request and Download EbicsResponse** - Using the HSM, the witness is able to create the EbicsRequest by signing it with the HSM: - $`{hsmSign}_{hsmtoken}(EbicsRquest) \rightarrow {sign}_{C_{priv}}({EbicsRequest})`$ +5. **Request and Download EbicsResponse** + Using the HSM, the Witness is able to create the EbicsRequest by signing it with the HSM: + ${hsmSign}_{hsmtoken}(EbicsRequest) \rightarrow {sign}_{C_{priv}}({EbicsRequest})$ 1. **Sign Encrypted Payload** - Create signature of encrypted payload: - $`{Signature}_{w_{priv}} = {Sign}_{W_{priv}}({Payload}_{enc})`$ + Witness creates signature of encrypted payload: + ${Signature}_{w_{priv}} = {Sign}_{W_{priv}}({Payload}_{enc})$ ## ZK proofing system ### Process Description -The witness uses hyperfridge and the HSM to create a SNARK proof with: -- $`{Payload}_{enc}, {SymKey}_{enc}, {XMLSignature}`$ -- $`{Signature}_{w_{priv}}`$ -- And decrypted transaction key ${SymKey}$ by calling $hsmDecrypt_{hsmtoken}(Symkey_{enc})$ +The Witness uses hyperfridge and the HSM to create a SNARK proof with: +- ${Payload}_{enc}, {SymKey}_{enc}, {XMLSignature}$ +- ${Signature}_{w_{priv}}$ +- And decrypted transaction key ${SymKey}$ by calling $hsmDecrypt_{hsmtoken}(Symkey_{enc})$ which is needed to speed up the proof-generation. -6. **Create STARK Proof**: ${ZKProof}_{ImageID}({PrivateInput}, {PublicInput}) \rightarrow {Commitment}$ +7. **Create STARK Proof**: ${ZKProof}_{ImageID}({PrivateInput}, {PublicInput}) \rightarrow {Commitment}$ - Private Inputs: - - Extracted form from ${EbicsRequest}$: + - Extracted from ${EbicsRequest}$: - ${Payload}_{enc}$: Encrypted payload, decrypt payload with ${SymKey}$ to extract the commitments. - ${SymKey}_{enc}$: Encrypted symetric transaction key, assert that the HSM-decrypted symetrical key is identical to the symetrical key of the document. - ${XMLSignature}, {}$: Signatures by the bank cover ${SymKey}$ @@ -80,9 +81,9 @@ The witness uses hyperfridge and the HSM to create a SNARK proof with: - Public Inputs: - IBAN, $B_{pub}$, $W_{pub}$ - - Commitment: Public input and data from $Payload$ and $EbicsResponse$: ${extractCommitment}({EbicsRequest}, {Payload})$ presented as a JSON document. + - Commitment: Public input and data from $Payload$ and $EbicsResponse$: ${extractCommitment}({EbicsRequest}, {Payload})$ presented as a JSON document embedded in the Risc-Zero STARK data structure. -The STARK presents a proof of computation. The computation is sealed (using Risk-Zero framework) and contains: +The STARK presents a proof of computation. The computation is sealed (using Risk-Zero framework) and has following properties: a. **Validate $SymKey$**: The Bank has generated $SymKey$ to encrypt the $Payload$ (clients data) - Verify $XMLSignature$ which contains $Symkey_{enc}$. @@ -96,30 +97,30 @@ The STARK presents a proof of computation. The computation is sealed (using Risk - Extract the data from payload and create the commitment. - Add public input to the commitment. - d. **A seal for the proofing algorithm - the $ImageID$** + d. **A seal for the proofing algorithm - the STARK based on an $ImageID$** - The seal (exact version and code) is identified by an $ImageID$. - See [Risk-Zero Proof System](https://dev.risczero.com/proof-system/). ### Verification -7. **Verification using Risk-Zero Recipe**: Other parties (like the Bank or external verifiers) can verify the zero-knowledge proof. +8. **Verification using Risk-Zero Receipt**: Other parties (like the Bank or external verifiers) can verify the zero-knowledge proof. - Verify proof with: ${VerifyZKProof_{ImageID}}() \rightarrow Commitment$. - Check the public input (IBAN, $B_{pub}$, $W_{pub}$). - (8.) **On-Chain verification with Groth16 SNARK**: - Risk-Zero privides a STARK to SNARK wrapper to support [on-chain verfication](https://www.risczero.com/news/on-chain-verification). - ## Key Benefits - **Privacy**: The Client's private key remain confidential throughout the process. No party has access to any of the others private keys. The witness is not able to see the payload. - **Security**: The signature from the Witness and the zero-knowledge proof mechanism ensure the security and integrity of the transaction without compromising sensitive information (transaction data). -- **Verifiability**: External parties can verify a bank statements legitimacy without accessing private keys or sensitive transaction details - on-chain or off-chain. +- **Verifiability**: External parties can verify a bank statements legitimacy without accessing private keys or sensitive transaction details - on-chain or off-chain. +- **Automation**: A TradFi transaction becomes a "signal" which can be presented, processed or triggered on any blockchain, fully automated. + ## Security Considerations -- The witness also acts as a signing proxy to create the $EbicsRequest$ which is necessary to trigger the download of $EbicsResponse$ which contains the $Payload$. -- The zero-knowledge proof allows the Client to prove knowledge of certain information without revealing the information itself. +- The witness also acts as a signing proxy to create the $EbicsRequest$ which is necessary to trigger the download of $EbicsResponse$ which contains the $Payload$. Witness and Client together are able to create fake proofs. +- The zero-knowledge proof allows the Client to prove knowledge of certain information without revealing the information itself and without violating the privacy of financial data. - The use of digital signatures by the Witness ensures the integrity and authenticity of the payload, which might not be necessary if the Ebics Standard implements its planned feature and will actually sign its payload. - The HSM must be tamper-resistant and capable of securely managing cryptographic keys and operations. @@ -132,7 +133,7 @@ The STARK presents a proof of computation. The computation is sealed (using Risk ### Proof transaction inclusion and Instant Payments -As we are able to prove the payload, we can create a proof for transaction inclusion. Means we can prove FIAT payments incoming and outgoing, both on-chain - similar to how cross-blockchain bridges work today. Most banks are operating on a daily basis which prevents interactive use-cases or would ask e.g. for optimistic implementation and addition funds to secure the FIAT bridge. But the EU is working on instant payments which would even allow instant interactive and non-interactive applications. This does not only apply to FIAT assets but also to any TradFi assets. For example, Maker DAO has invested 400 mUSD in a Blackrock ETF for traditional assets. Hyperfridge would be able to create proof of assets and do balancing of investments (invest and divest) automatically. Note that using Ebics and standard banking functionality comes at no cost or transaction fees. +As we are able to prove the payload, we can create a proof for transaction inclusion. Means we can prove FIAT payments incoming and outgoing, both on-chain - similar to how cross-blockchain bridges work today. Most banks are operating on a daily basis which prevents interactive use-cases or would ask e.g. for optimistic implementation and addition funds to secure the FIAT bridge. But the EU (European Union) is working on instant payments which would even allow instant interactive and non-interactive applications. This does not only apply to FIAT assets but also to any TradFi assets. For example, Maker DAO has invested 400 mUSD in a Blackrock ETF for traditional assets. Hyperfridge would be able to create proof of assets and do balancing of investments (invest and divest) automatically. Note that using Ebics and standard banking functionality comes at no cost or transaction fees. ### Open-API Banking and Payment-API (e.g. Stripe) @@ -142,4 +143,3 @@ The principles presented here can be applied to other Open Banking Standards as ### Witness, HSM and other secure modules The Witness for hyperfridge needs an HSM to sign data. One might think of Apple Secure Enclave to enable new use cases where a simple Phone or Laptop can act as a witness. - diff --git a/docs/guest-hyperfridge.md b/docs/guest-hyperfridge.md index 5b41a6d..2b1645b 100644 --- a/docs/guest-hyperfridge.md +++ b/docs/guest-hyperfridge.md @@ -13,11 +13,11 @@ because you would need the private keys of both parties. The EBICS protocol (Ele How were the goals achieved with this implementation? -- **Proving soundness**: Singing payload data is still optional in the Ebics standard, so a bank backend needs to support that explicitly, and we cannot fully rely on the standard. As an alternative we introduce the concept of a "data processor" as an additional entity next to the bank and the client. The data processor downloads and signs the document (and payload) and acts as third-party witness. +- **Proving soundness**: Singing payload data is still optional in the EBICS standard, so a bank backend needs to support that explicitly, and we cannot fully rely on the standard. As an alternative we introduce the concept of a "data processor" as an additional entity next to the bank and the client. The data processor downloads and signs the document (and payload) and acts as third-party witness. - **Privacy**: Fully achieved - it's trivial so include or not include data in proofs ([receipts][receipt]). - **Low execution time**: Execution time is around 45 minutes to generate a proof. This is sufficient for current scenarios but too much in case of instant payments. -## Fundamental properties of the banking interface (ISO20022 and Ebics) +## Fundamental properties of the banking interface (ISO20022 and EBICS) The basic idea is the following: Whenever the bank (the banking API) is transmitting documents, it sends its data with a signature - using [XML encryption standards](https://www.w3.org/TR/xmlenc-core1/). For example a response document for a daily statement of balance and transactions would contain a section like this: diff --git a/docs/host.md b/docs/host.md index 4428719..d5da8ac 100644 --- a/docs/host.md +++ b/docs/host.md @@ -15,7 +15,7 @@ importantly to other systems or ledgers with Smart Contracts. As a result, you c use Smart Contracts to "wrap" functionality around TradFi accounts, similar like you can do it today with blockchain wallets and cryptocurrencies. -## Verifier +## Create a STARK proof with host program ### Usage of host program @@ -30,25 +30,9 @@ To run the host program, you need to provide the following arguments in order: 1. **EBICS Response XML**: The EBICS response in XML format. 2. **Bank Public Key (PEM Format)**: The public key of the bank in PEM format. 3. **User Private Key (PEM Format)**: The private key of the user in PEM format. +4. **Witness Private Key (PEM Format)**: The public key of the witness in PEM format. - -The arguments should be provided in the exact order as mentioned above. - -#### Example command - -Without data from the banking backend and valid keys, the program would not be able to -do something meaningful. So included test data to see how it works: - -```bash -host ../data/test/test.xml ../data/bank_public.pem ../data/client.pem -``` - -- **`../data/test/test.xml`**- '': The document provided by the backend of the bank which contains the payload -(bank statements), transaction keys to decrypt the payload and other data singed by the bank. -- **`../data/bank_public.pem`**: The public key of the bank. Due to signing needs, we also need the -private key of the bank to test data. -- **`../data/client.pem`**: The private key of the client which is needed to decrypt the transaction -key of 'text.xml'. +See [Testing Guide](INSTRUCTIONS.md) for exmples how to use the command line. #### Files required @@ -67,12 +51,10 @@ Replace `` with the path and base name of your EBICS respons if your EBICS response XML file is `../data/test/test.xml`, the decrypted transaction key should be named `../data/test/test-decrypted_tx_key.binary`. -See further below for more information on generating the input files. -### Output +### Output of the Receipt -Upon successful execution, the program prints a receipt in JSON format stored under 'data/test.xml-Receipt' -'data/-Receipt'. +Upon successful execution, the program prints a receipt in JSON format stored under `data/test.xml-Receipt/` where test.xml is replaced by the filename of your EbicsResponse XML document. ### How to use @@ -123,7 +105,7 @@ This document specifies a process for encrypting data and representing the resul ### checkResponse.sh and other supporting scripts - [checkResponse.sh](../data/checkResponse.sh) This script can be used to pre-check signatures and to create the necessary -input files. Working with Ebics XML may be challenging, please reach out if you are stuck. +input files. Working with EBICS XML may be challenging, please reach out if you are stuck. - [createTestResponse.sh](../data/createTestResponse.sh) This script can is used to generate test data. It creates and signs documents which usually is generated by the bank, and at the end it calls the script `checkResponse.sh` which generates the input documents for the verifier. diff --git a/docs/milestones.md b/docs/milestones.md index 92b52f2..70de325 100644 --- a/docs/milestones.md +++ b/docs/milestones.md @@ -9,9 +9,9 @@ | 0b. | Documentation | We will provide both inline documentation of the code, a basic tutorial and a markdown description of the proof system. | [Link to Documentation Root](https://github.com/element36-io/hyperfridge-r0/blob/main/README.md)| | 0c. | Testing Guide | Provide unit tests of core functions and test data to ensure functionality. In the guide, we will describe how to run these tests. | [Link to Testing Guide](https://github.com/element36-io/hyperfridge-r0/blob/main/docs/INSTRUCTIONS.md) | | 0d. | Docker | We will provide a Dockerfile(s) that can be used to test all the functionality delivered with this milestone. | [Dockerhub](https://hub.docker.com/repository/docker/e36io/hyperfridge-r0/general) [Testing Guide](https://github.com/element36-io/hyperfridge-r0/blob/main/docs/INSTRUCTIONS.md) -| 1a. | risc0 Guest Program | Code (circuit) to generate the proof, later used by the proving system. Secret input of [Guest Program](https://dev.risczero.com/terminology#guest-program): Ebics envelope as XML and Z53/Camt53 file(s) as ZIP binary - see XML examples above. The Public input is: Public Certificate of the Bank or name of bank, bank account number, balance and date. The [journal](https://dev.risczero.com/terminology#journal) will contain balance, currency, timestamp in the ebics-envelope, timestamp of the proof, client-account-number, Bank-ID and sequence number of the bank-statement. The circuit will check the hash of the (zipped) Z53 documents and compares it with the data given in the ebicsRequest. It checks the signature of the Ebics request and the signed hash of the ZIP file using crypto standards X002 and E002. "X002" refers to RSA signature key with a key length of 2048 bits, "E002" defines RSA algorithm for encryption using ECB (Electronic Codebook) and PKCS#1 v1.5 padding.| [Repo](https://github.com/element36-io/hyperfridge-r0/tree/main/methods/guest) +| 1a. | risc0 Guest Program | Code (circuit) to generate the proof, later used by the proving system. Secret input of [Guest Program](https://dev.risczero.com/terminology#guest-program): EBICS envelope as XML and Z53/Camt53 file(s) as ZIP binary - see XML examples above. The Public input is: Public Certificate of the Bank or name of bank, bank account number, balance and date. The [journal](https://dev.risczero.com/terminology#journal) will contain balance, currency, timestamp in the ebics-envelope, timestamp of the proof, client-account-number, Bank-ID and sequence number of the bank-statement. The circuit will check the hash of the (zipped) Z53 documents and compares it with the data given in the ebicsRequest. It checks the signature of the EBICS request and the signed hash of the ZIP file using crypto standards X002 and E002. "X002" refers to RSA signature key with a key length of 2048 bits, "E002" defines RSA algorithm for encryption using ECB (Electronic Codebook) and PKCS#1 v1.5 padding.| [Repo](https://github.com/element36-io/hyperfridge-r0/tree/main/methods/guest) | 1b. | Generate Receipt | Generate [receipt](https://dev.risczero.com/terminology#receipt) which proves that the computation (e.g. balance) is correct and signed by the bank. | [Testing Guide](https://github.com/element36-io/hyperfridge-r0/blob/main/docs/INSTRUCTIONS.md)| | 1c. | Validator | Code to validate the receipt. | [Repo](https://github.com/element36-io/hyperfridge-r0/tree/main/verifier)| -| 1d. | Hyperfridge Crate | The crate to create and validate recipes (ZKPs), wrapping the functionality. | [Repo](https://github.com/element36-io/hyperfridge-r0/tree/main/host) +| 1d. | Hyperfridge Crate | The crate to create and validate receip (ZKPs), wrapping the functionality. | [Repo](https://github.com/element36-io/hyperfridge-r0/tree/main/host) | 2. | Unit Tests | We will add unit tests and test data for creating and validating proofs which includes edge cases like wrong balance claims or faulty signature of the bank. | [Testing Guide](https://github.com/element36-io/hyperfridge-r0/blob/main/docs/INSTRUCTIONS.md) | | 3. | Performance Benchmark | Present a table with performance metrics, so that hyperfride proofing times can be interpolated with data from [risc-zero](https://dev.risczero.com/datasheet.pdf). | [Benchmarks](runtime.md) | \ No newline at end of file diff --git a/docs/runtime.md b/docs/runtime.md index 659df87..35393d2 100644 --- a/docs/runtime.md +++ b/docs/runtime.md @@ -1,15 +1,14 @@ - # Runtime measurements + # Performance measurements Conclusions: -- -- It takes hours without hardware acceleration to produce the STARK on strong laptop hardware, but it can be considered fast-enough, because the banking backend updates its data only once a working day. Cost is around 2-6 USD per proof (Cost basis AWS EC2 instance c5d.12xlarge, no CUCA) -- With hardware acceleration (CUCA) we *estimate* around 10-20 minutes. We could not test hardware accelartion due to compile errors with Risc-Zero framework and CUDA drivers. -- RSA framework is biggest bottleneck. It might be interesting to apply the [Big-Integer Patch](https://github.com/risc0/RustCrypto-crypto-bigint/tree/risczero) of Risc0 to the RSA crate, but this was not investigated further. -### Reference Hardware (gaming Laptop) +- With hardware acceleration (CUDA) we have execution times for a proof around **10-20 minutes**. We could not test hardware acceleration due to lack of memory, but testing with other examples shows dramatic speed-up. +- Cost is estimated around 0.5 USD per proof with hardware acceleration. Upper bound (no hardware acceleration) is around 2-6 USD per proof (Cost basis AWS EC2 instance c5d.12xlarge, no CUDA) +- RSA framework is biggest bottleneck. It might be interesting to apply the [Big-Integer Patch](https://github.com/risc0/RustCrypto-crypto-bigint/tree/risczero) of Risc0 to the RSA crate, but this was not investigated further. -Hardware: Lenovo Legion 5 Pro, 32 GB RAM, Geforce 4070 (cudo NOT activitaed) +### Reference Hardware +Hardware: Gaming Laptop, Lenovo Legion 5 Pro, 32 GB RAM, Geforce 4070 (8GB) vendor_id : GenuineIntel cpu family : 6 model : 183 @@ -18,7 +17,7 @@ cpu MHz : 1984.575 cache size : 30720 KB cpu cores : 16 -Comparising with Risk Zero benchmarks [here](https://dev.risczero.com/datasheet.pdf): +Comparising with Risk Zero benchmarks [here](https://dev.risczero.com/datasheet.pdf): | Cycles | Duration | RAM | Seal | Speed | | ---- | ---- | ---- | ---- | ---- | @@ -31,7 +30,17 @@ Comparising with Risk Zero benchmarks [here](https://dev.risczero.com/datasheet. | 4096k | 12:41.5 | 7.56GB | 1.1MB | 5.5khz | -Running a roundtrip of proof generation of validation gives following numbers on the reference hardware: +With CUDA enabled we see roughly 10-20x speedup until memory runs out: + +| Cycles | Duration | RAM | Seal | Speed | +| ---- | ---- | ---- | ---- | ---- | +| 64k | 794.7ms | 472.4MB | 215.3kB | 82.5khz | +| 128k | 1.36s | 944.8MB | 238.3kB | 96.3khz | +| 256k | 2.8s | 1.89GB | 250kB | 93.7khz | + + + +Running a roundtrip of proof generation of validation gives following numbers on the reference hardware (no CUDA): ``` RUST_LOG="executor=info" RUST_BACKTRACE=1 RISC0_DEV_MODE=true cargo test -- --nocapture @@ -44,11 +53,6 @@ RUST_LOG="executor=info" RUST_BACKTRACE=1 RISC0_DEV_MODE=true cargo test -- -- Runtime for genation of Proof: 12055.32s - around ***200 minutes*** without hardware acceleration. -Notes: -- Risc0 reports 37 segments which are parallized using [continuation](https://www.risczero.com/news/continuations) on 16 cores (24 virtual cores - see hardware spec above). Using hardware with 48 cores execution time would likely be cut half to ***100 minutes*** if CPUs have same speed. -- Hardware acceleration (CUDA) not tested. - - Cycle most expensive calculations: - **Cycle count verify_bank_signature 10687k**: Checking RSA signature of Bank. diff --git a/host/src/main.rs b/host/src/main.rs index 40a2392..77559cb 100644 --- a/host/src/main.rs +++ b/host/src/main.rs @@ -15,7 +15,8 @@ use std::process::Command; static mut VERBOSE: bool = false; // print verbose -macro_rules! v { +/// Print the message if `VERBOSE` mode +macro_rules! print_verbose { ($($arg:tt)*) => { unsafe { if VERBOSE { @@ -36,6 +37,7 @@ struct Commitment { stmts: Vec, } +/// Struct for the statement data in the commitment #[derive(Deserialize, Debug)] #[allow(dead_code)] struct Stmt { @@ -123,7 +125,7 @@ fn main() { let _script_full_path = script_dir.join(script_file_stem); - v!( + print_verbose!( "calling {} xml_file={} pub_bank={} client={} pub_witness{}", &script_path.to_str().unwrap(), &camt53_filename, @@ -143,7 +145,7 @@ fn main() { .expect("failed to execute script"); if output.status.success() { - v!("Script {:?} executed successfully.", script_path.clone()); + print_verbose!("Script {:?} executed successfully.", script_path.clone()); } else { eprintln!("Script output:"); eprintln!("stdout:\n{}", String::from_utf8_lossy(&output.stdout)); @@ -157,7 +159,7 @@ fn main() { } } Some(Commands::Test) => { - v!("Proofing with test data."); + print_verbose!("Proofing with test data."); pub_bank_pem_filename = TEST_BANKKEY.to_string(); client_pem_filename = TEST_CLIENTKEY.to_string(); pub_witness_pem_filename = TEST_WITNESSKEY.to_string(); @@ -191,7 +193,7 @@ fn main() { // key, encrypt it and check if it matches with the encrypted transaction key // in the XML file. let decrypted_tx_key_bin_filename = format!("{}-TransactionKeyDecrypt.bin", camt53_filename); - v!("open {}", &decrypted_tx_key_bin_filename); + print_verbose!("open {}", &decrypted_tx_key_bin_filename); let decrypted_tx_key_bin: Vec = fs::read(&decrypted_tx_key_bin_filename).unwrap_or_else(|_| { @@ -235,7 +237,7 @@ fn main() { let receipt = receipt_result.unwrap(); let receipt_json_string = serde_json::to_string(&receipt).expect("Failed to serialize receipt in main"); - v!("Receipt result: {:?}", &receipt_json_string); + print_verbose!("Receipt result: {:?}", &receipt_json_string); // let commitment_string = std::str::from_utf8(receipt.journal.bytes.clone()) // .expect("Failed to convert bytes to string from journal in main"); @@ -251,7 +253,7 @@ fn main() { .expect("Failed to convert bytes to string from journal in main") }; - v!("Receipt with public commitment: {} ", &(commitment_string)); + print_verbose!("Receipt with public commitment: {} ", &(commitment_string)); let commitment: Result = serde_json::from_str(&commitment_string); @@ -271,7 +273,7 @@ fn main() { } Err(e) => { receipt_file_id = "commit_json_error".to_owned(); - v!("Receipt successful generated, but deserializing JSON for commitment failed. Error: {}., Json: {}.", e,commitment_string) + print_verbose!("Receipt successful generated, but deserializing JSON for commitment failed. Error: {}., Json: {}.", e,commitment_string) } } @@ -290,7 +292,7 @@ fn main() { file.write_all(receipt_json_string.as_bytes()) .unwrap_or_else(|_| panic!("Unable to write data in file {} (main)", &file_name)); - v!(" wrote receipt to {}", &file_name); + print_verbose!(" wrote receipt to {}", &file_name); // filename with -latest.json let file_name = format!( @@ -306,10 +308,10 @@ fn main() { panic!("Unable to write data in latest file {} (main)", &file_name) }); - v!(" wrote receipt to {}", &file_name); + print_verbose!(" wrote receipt to {}", &file_name); } Err(e) => { - v!("Receipt error in proove_camt53 {:?}", e); + print_verbose!("Receipt error in proove_camt53 {:?}", e); panic!("Creating the proof failed {}", e) } } @@ -343,7 +345,7 @@ fn proove_camt53( pub_witness_pem: &str, host_info: &str, ) -> Result { - v!("start: {}", Local::now().format("%Y-%m-%d %H:%M:%S")); + print_verbose!("start: {}", Local::now().format("%Y-%m-%d %H:%M:%S")); // write image ID to filesystem let _ = write_image_id(); @@ -362,7 +364,7 @@ fn proove_camt53( // .write(&modu_hex).unwrap() // https://docs.rs/risc0-zkvm/latest/risc0_zkvm/struct.ExecutorEnvBuilder.html - v!("Starting guest code, load environment"); + print_verbose!("Starting guest code, load environment"); env_logger::init(); let pem = parse(bank_public_key_x002_pem).expect("Failed to parse bank public key PEM"); let bank_public_key = RsaPublicKey::from_public_key_pem(&pem::encode(&pem)) @@ -406,16 +408,16 @@ fn proove_camt53( // Obtain the default prover. let prover = default_prover(); - v!("prove hyperfridge elf "); + print_verbose!("prove hyperfridge elf "); // generate receipt let receipt_result = prover.prove_elf(env, HYPERFRIDGE_ELF); profiler.finalize(); let report = profiler.encode_to_vec(); - v!("write profile size {}", report.len()); + print_verbose!("write profile size {}", report.len()); std::fs::write("./profile-output", &report).expect("Unable to write profiling output"); let image_id_hex = get_image_id_hex(); - v!( + print_verbose!( "got the receipt of the prove , id first 32u {} binary size of ELF binary {}k", image_id_hex, HYPERFRIDGE_ELF.len() / 1000 @@ -611,16 +613,16 @@ mod tests { match &receipt_result { Ok(_val) => { - // v!("Receipt result: {}", val);_ + // print_verbose!("Receipt result: {}", val);_ let receipt = receipt_result.unwrap(); receipt .verify(HYPERFRIDGE_ID) .expect("Verification of receipt failed in test"); let receipt_json = serde_json::to_string(&receipt).expect("Failed to serialize receipt"); - v!("Receipt result: {:?}", &receipt_json); + print_verbose!("Receipt result: {:?}", &receipt_json); let journal = receipt.journal; - v!( + print_verbose!( "Receipt result (commitment) {}: ", &(journal.decode::().unwrap()) ); diff --git a/methods/guest/src/main.rs b/methods/guest/src/main.rs index a82103c..8e49016 100644 --- a/methods/guest/src/main.rs +++ b/methods/guest/src/main.rs @@ -22,6 +22,12 @@ use xmlparser::{ElementEnd, Token, Tokenizer}; use hex::FromHex; +use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, KeyIvInit}; +type Aes128CbcDec = cbc::Decryptor; +// `no-std` is not a priority for now, so this can stay +use std::io::{Cursor, Read}; +use zip::ZipArchive; + #[cfg(not(feature = "debug_mode"))] risc0_zkvm::guest::entry!(main); @@ -30,7 +36,7 @@ mod test_xmlparse; static mut VERBOSE: bool = false; // print verbose or not -macro_rules! v { +macro_rules! print_verbose { ($($arg:tt)*) => { unsafe { if VERBOSE { @@ -69,6 +75,7 @@ struct Document { grp_hdr: GrpHdr, // creatin time stmts: Vec, } + /// GrpHdr structure of a Camt53 XML respose #[derive(Debug, Default, Clone)] struct GrpHdr { @@ -77,6 +84,7 @@ struct GrpHdr { pg_nb: i8, last_pg_ind: bool, } + /// Stmt structure of a Camt53 XML respose #[derive(Debug, Default, Clone)] struct Stmt { @@ -87,6 +95,7 @@ struct Stmt { to_dt_tm: String, balances: Vec, } + /// Balance structure of a Camt53 XML respose /// code or proprietory - OPBD = opening balance,CLBD is closing balance /// cdt_dbt_ind - creit or debit indicator - plus or minus of the balance @@ -99,7 +108,6 @@ struct Balance { cdt_dbt_ind: String, // cdt_dbt_ind - creit or debit indicator - plus or minus of the balance } -//#[cfg(not(tarpaulin))] pub fn main() { let signed_info_xml_c14n: String = env::read(); let authenticated_xml_c14n: String = env::read(); @@ -127,10 +135,10 @@ pub fn main() { // let modu = U256::from_be_hex(&pub_bank_mod); let pub_bank = RsaPublicKey::new(modu, exp).expect("Failed to create public key in main"); - v!("pub_bank {} bit", pub_bank.n().bits()); + print_verbose!("pub_bank {} bit", pub_bank.n().bits()); let client_key = RsaPrivateKey::from_pkcs8_pem(&client_key_pem) .expect("Failed to create client_key_pem in main"); - v!("client_key {} bit", client_key.n().bits()); + print_verbose!("client_key {} bit", client_key.n().bits()); let pub_witness = RsaPublicKey::from_public_key_pem(&pub_witness_pem) .expect("Failed to create pub_witness_key in main"); @@ -153,16 +161,14 @@ pub fn main() { &pub_witness, ); - v!(" Cycle count {}k", (env::get_cycle_count()) / 1000); - //env::log("proof done - log entry"); // writes to journal - we may communicate with host here + print_verbose!(" Cycle count {}k", (env::get_cycle_count()) / 1000); let mut commitments = Vec::new(); // public committed data, that is what we want to prove for document in documents { // stmts[0] is ok, because only one - we filtered IBAN already - assert_eq!( - document.stmts.len(), - 1, + assert!( + document.stmts.len() == 1, "only one IBAN should only give one Stmt xml entry", ); let commitment = format!( @@ -192,7 +198,7 @@ pub fn main() { &pub_client_pem.replace("\n", "\\n").replace("\r", "\\r"), &commitments.join(",") ); - v!("Commitment for receipt: {}", &final_commitment); + print_verbose!("Commitment for receipt: {}", &final_commitment); env::commit(&final_commitment); } @@ -219,7 +225,7 @@ fn load( pub_witness: &RsaPublicKey, ) -> Vec { // star is with 1586k - v!(" Cycle count start {}k", (env::get_cycle_count()) / 1000); + print_verbose!(" Cycle count start {}k", (env::get_cycle_count()) / 1000); let request = parse_ebics_response( authenticated_xml_c14n, @@ -227,14 +233,14 @@ fn load( signature_value_xml, order_data_xml, ); - v!( + print_verbose!( " > Cycle count parse_ebics_response {}k", (env::get_cycle_count()) / 1000 ); // cycle count 1864k (plus 3k) verify_bank_signature(pub_bank, &request); - v!( + print_verbose!( " Cycle count verify_bank_signature {}k", (env::get_cycle_count()) / 1000 ); @@ -242,7 +248,7 @@ fn load( // cycle count 23336k (plus 10k) let transaction_key = decrypt_transaction_key(&request, client_key, decrypted_tx_key); - v!( + print_verbose!( " Cycle count decrypt_transaction_key {}k", (env::get_cycle_count()) / 1000 ); @@ -255,7 +261,7 @@ fn load( witness_signature_bytes, pub_witness, ); - v!( + print_verbose!( " Cycle count decrypt_order_data {}k", (env::get_cycle_count()) / 1000 ); @@ -276,25 +282,25 @@ fn load( if !document.stmts.is_empty() { documents.push(document); } else { - v!( + print_verbose!( " IBAN {} not found, ignore camt document {}", &iban, String::from_utf8_lossy(&order_data[index - 1]) ); } - v!( + print_verbose!( " Cycle count for camt document {}k", (env::get_cycle_count()) / 1000 ); } else { - v!( + print_verbose!( " processing camt document {}", String::from_utf8_lossy(&order_data[index]) ); } } - v!( + print_verbose!( " Cycle count parse_camt53 {}k", (env::get_cycle_count()) / 1000 ); @@ -379,7 +385,7 @@ fn get_client_key_hex(pk: &RsaPublicKey) -> String { /// required canonicalization [XML-C14N] of this specification does not /// change URIs. fn verify_bank_signature(pub_bank: &RsaPublicKey, request: &Request) { - v!("verify bank signature"); + print_verbose!("verify bank signature"); // Decode the signature let signature_value_bytes = general_purpose::STANDARD .decode(&request.signature_value_b64) @@ -395,9 +401,9 @@ fn verify_bank_signature(pub_bank: &RsaPublicKey, request: &Request) { // prefix. let scheme = Pkcs1v15Sign::new::(); - // v!("{} {}",request.signed_info_hashed.len(),signature_value_bytes.len()); - // v!("hash digest {} ", &*Impl::hash_bytes(&request.signed_info_hashed)); - // v!("hash signature {} ", &*Impl::hash_bytes(&signature_value_bytes)); + // print_verbose!("{} {}",request.signed_info_hashed.len(),signature_value_bytes.len()); + // print_verbose!("hash digest {} ", &*Impl::hash_bytes(&request.signed_info_hashed)); + // print_verbose!("hash signature {} ", &*Impl::hash_bytes(&signature_value_bytes)); // Verify the signature let res = pub_bank.verify( @@ -405,9 +411,9 @@ fn verify_bank_signature(pub_bank: &RsaPublicKey, request: &Request) { &request.signed_info_hashed, &signature_value_bytes, ); - // v!(" res ----> {:?}",&res); + // print_verbose!(" res ----> {:?}",&res); match res { - Ok(_) => v!(" bank Signature is verified"), + Ok(_) => print_verbose!(" bank Signature is verified"), Err(e) => { eprintln!(" ---> error {:?}", e); panic!(" bank Signature could not be verified") @@ -450,12 +456,12 @@ fn parse_ebics_response( for token in tokens { match token { Ok(Token::ElementStart { local, .. }) => { - //v!(" open tag as_str {:?}", local.as_str()); + //print_verbose!(" open tag as_str {:?}", local.as_str()); curr_tag = local.as_str(); } Ok(Token::ElementEnd { end, .. }) => { if let ElementEnd::Close(.., _local) = end { - // v!(" close tag as_str {:?}", _local.as_str()); + // print_verbose!(" close tag as_str {:?}", _local.as_str()); // handling Close variant curr_tag = ""; } @@ -504,34 +510,28 @@ fn parse_ebics_response( } } - assert_ne!( - digest_value_b64.len(), - 0, + assert!( + digest_value_b64.len() != 0, "Asserting longer than 0: digest_value_b64" ); - assert_ne!( - transaction_key_b64.len(), - 0, + assert!( + transaction_key_b64.len() != 0, "Asserting longer than 0: transaction_key_b64" ); - assert_ne!( - bank_timestamp.len(), - 0, + assert!( + bank_timestamp.len() != 0, "Asserting longer than 0: bank_timestamp" ); - assert_ne!( - signature_value_b64.len(), - 0, + assert!( + signature_value_b64.len() != 0, "Asserting longer than 0: signature_value_b64" ); - assert_ne!( - signed_info_hashed.len(), - 0, + assert!( + signed_info_hashed.len() != 0, "Asserting longer than 0: signed_info_hashed" ); - assert_ne!( - order_data_b64.len(), - 0, + assert!( + order_data_b64.len() != 0, "Asserting longer than 0: order_data_b64" ); @@ -586,7 +586,7 @@ fn decrypt_transaction_key( if !decrypted_tx_key.is_empty() { // its still padded - v!("WARNING: binary transaction key was provided - we use this to decrypt"); + print_verbose!("WARNING: binary transaction key was provided - we use this to decrypt"); // Do some check on the provided key - ensure that the data is long enough and has the correct PKCS#1 v1.5 padding prefix. assert!( @@ -607,14 +607,14 @@ fn decrypt_transaction_key( // https://docs.rs/rsa/latest/rsa/hazmat/fn.rsa_encrypt.html // Raw RSA encryption and "hazmat" is considered "OK", because do do not use the encryption. // We check if if provided decrypted key was using the decrypted key in the XML as source. - v!( + print_verbose!( " Cycle count before rsa_encrypt {}k", (env::get_cycle_count()) / 1000 ); let encrypted_recreated = rsa::hazmat::rsa_encrypt(&pub_key, &BigUint::from_bytes_be(decrypted_tx_key)).unwrap(); - v!( + print_verbose!( " Cycle count after rsa_encrypt {}k", (env::get_cycle_count()) / 1000 ); @@ -643,13 +643,13 @@ fn decrypt_transaction_key( // remove pemm feature, initialize with numbers - less code, more efficent? - v!(" start decrypt transaction key with Pkcs1v15 Rsa"); + print_verbose!(" start decrypt transaction key with Pkcs1v15 Rsa"); // Decrypt with PKCS1 padding let decrypted_data = client_key.decrypt(Pkcs1v15Encrypt, &transaction_key_bin); match decrypted_data { Ok(res) => { - v!(" transaction key to decrypt payload could be decrypted"); + print_verbose!(" transaction key to decrypt payload could be decrypted"); res } Err(e) => { @@ -659,12 +659,6 @@ fn decrypt_transaction_key( } } -use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, KeyIvInit}; -type Aes128CbcDec = cbc::Decryptor; -// Dastan - can we get rid of this? -use std::io::{Cursor, Read}; -use zip::ZipArchive; - /// using the decrypted transaction key, lets decrypt the payload. /// The payload is considered a stream which is compressed with the deflate alogrithm. /// The stream is actually a ZIP file, which containts the XML documents which hold the @@ -678,7 +672,7 @@ fn decrypt_order_data( witness_signature_bytes: &[u8], pub_witness: &RsaPublicKey, ) -> Vec> { - v!(" decrypting payload with transaction key"); + print_verbose!(" decrypting payload with transaction key"); let order_data_bin = general_purpose::STANDARD .decode(&request.order_data_b64) @@ -686,30 +680,30 @@ fn decrypt_order_data( let sha = *Impl::hash_bytes(&order_data_bin); - v!(" verify the verify_order_data_signature by witness"); + print_verbose!(" verify the verify_order_data_signature by witness"); // check witness signature // We checked for Schema A005 which enforces: let scheme = Pkcs1v15Sign::new::(); // Verify the signature - v!( + print_verbose!( " Cycle count before pub_witness.verify( {}k", (env::get_cycle_count()) / 1000 ); let res = pub_witness.verify(scheme, sha.as_bytes(), witness_signature_bytes); - v!( + print_verbose!( " Cycle count after pub_witness.verify( {}k", (env::get_cycle_count()) / 1000 ); match res { - Ok(_) => v!(" Order Data is verified"), + Ok(_) => print_verbose!(" Order Data is verified"), Err(e) => { eprintln!(" ---> error {:?}", e); panic!(" Order Data Signature could not be verified") } }; - v!( + print_verbose!( " Cycle count before .decrypt_padded_b2b_mut( {}k", (env::get_cycle_count()) / 1000 @@ -731,7 +725,7 @@ fn decrypt_order_data( .decrypt_padded_b2b_mut::(&order_data_bin, &mut result_bytes) .unwrap(); - v!( + print_verbose!( " Cycle count after .decrypt_padded_b2b_mut( {}k", (env::get_cycle_count()) / 1000 @@ -766,7 +760,7 @@ fn decrypt_order_data( /// as base64 in the Ebics Response XML. /// It get information from ISO20022 camt53 which hold bank data. fn parse_camt53(camt53_file: &str) -> Document { - v!(" parsing payload to extract data to commit"); + print_verbose!(" parsing payload to extract data to commit"); let mut tag_stack: Vec = Vec::new(); let mut current_balance = Balance::default(); let mut grp_header = GrpHdr::default(); @@ -781,12 +775,12 @@ fn parse_camt53(camt53_file: &str) -> Document { Ok(Token::ElementStart { local, .. }) => { current_tag = local.to_string(); tag_stack.push(local.to_string()); - // v!(" open tag as_str {:?} ", local.as_str()); + // print_verbose!(" open tag as_str {:?} ", local.as_str()); } Ok(Token::ElementEnd { end, .. }) => { if let ElementEnd::Close(.., local) = end { if let Some(_tag) = tag_stack.pop() { - // v!("End Tag: {}", _tag); + // print_verbose!("End Tag: {}", _tag); }; if local == "Bal" { current_stmt.balances.push(current_balance); @@ -799,7 +793,7 @@ fn parse_camt53(camt53_file: &str) -> Document { } Ok(Token::Text { text }) => { if let Some(_current_tag) = tag_stack.last() { - //v!("Text for {}: {}", _current_tag, text); + //print_verbose!("Text for {}: {}", _current_tag, text); }; //35e75effeaa74f579f97c8121bfa68ad2023-11-29T22:54:31.6579278+01:001true