From a1281ddcaa757b2007ecd5b7bc1d6082d2eb1d7a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:33:22 -0700 Subject: [PATCH 01/22] chore: update README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index a5ca0dc9a..f1d6a0034 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ +

+ Pluto Logo +
+ Pluto +

+

+ Telegram + Docs + License +

+ +--- + # Web Prover [![web-prover workflow](https://github.com/pluto/web-prover/actions/workflows/web-prover.yaml/badge.svg)](https://github.com/pluto/web-prover/actions/workflows/web-prover.yaml) From e7e4804b40a003182c9dfd986ffd3e592eccace2 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:36:50 -0700 Subject: [PATCH 02/22] refactor: rename crates/paths --- .../staging-deploy-gcs/notary-config.toml | 4 +- .../staging-deploy/notary-config.toml | 2 +- Cargo.lock | 146 +++++++++--------- Cargo.toml | 30 ++-- book/book.toml | 6 +- client/Cargo.toml | 24 +-- {web-prover-core => core}/Cargo.toml | 8 +- {web-prover-core => core}/src/errors.rs | 0 {web-prover-core => core}/src/http.rs | 0 {web-prover-core => core}/src/lib.rs | 0 {web-prover-core => core}/src/manifest.rs | 0 {web-prover-core => core}/src/proof.rs | 0 {web-prover-core => core}/src/test_utils.rs | 0 fixture/notary-config.toml | 2 +- notary/Cargo.toml | 42 ++--- taplo.toml | 2 +- tests/Cargo.toml | 4 +- 17 files changed, 135 insertions(+), 135 deletions(-) rename {web-prover-core => core}/Cargo.toml (100%) rename {web-prover-core => core}/src/errors.rs (100%) rename {web-prover-core => core}/src/http.rs (100%) rename {web-prover-core => core}/src/lib.rs (100%) rename {web-prover-core => core}/src/manifest.rs (100%) rename {web-prover-core => core}/src/proof.rs (100%) rename {web-prover-core => core}/src/test_utils.rs (100%) diff --git a/.github/workflows/staging-deploy-gcs/notary-config.toml b/.github/workflows/staging-deploy-gcs/notary-config.toml index 2287aa1a5..5d71817e7 100644 --- a/.github/workflows/staging-deploy-gcs/notary-config.toml +++ b/.github/workflows/staging-deploy-gcs/notary-config.toml @@ -1,2 +1,2 @@ -listen ="0.0.0.0:443" -acme_email ="eng@pluto.xyz" \ No newline at end of file +acme_email="eng@pluto.xyz" +listen ="0.0.0.0:443" diff --git a/.github/workflows/staging-deploy/notary-config.toml b/.github/workflows/staging-deploy/notary-config.toml index 3489960d9..109ed81d0 100644 --- a/.github/workflows/staging-deploy/notary-config.toml +++ b/.github/workflows/staging-deploy/notary-config.toml @@ -1,3 +1,3 @@ +acme_email ="eng@pluto.xyz" listen ="0.0.0.0:443" notary_signing_key="/opt/notary/etc/fixture/certs/notary.key" -acme_email ="eng@pluto.xyz" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 07565980f..a913814e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,36 +706,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "client" -version = "0.7.0" -dependencies = [ - "base64 0.22.1", - "bytes", - "clap", - "futures", - "hex", - "http-body-util", - "hyper", - "hyper-util", - "reqwest", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_with", - "thiserror 1.0.69", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "tracing-subscriber", - "url", - "uuid", - "web-prover-core", - "webpki-roots", -] - [[package]] name = "colorchoice" version = "1.0.3" @@ -2018,49 +1988,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "notary" -version = "0.7.0" -dependencies = [ - "alloy-primitives", - "async-trait", - "axum", - "axum-core", - "base64 0.21.7", - "chrono", - "clap", - "client", - "config", - "eyre", - "futures", - "futures-util", - "hex", - "http", - "hyper", - "hyper-util", - "k256", - "nom", - "reqwest", - "rs_merkle", - "rustls", - "rustls-acme", - "rustls-pemfile", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-rustls", - "tokio-stream", - "tokio-util", - "tower 0.4.13", - "tower-http", - "tower-service", - "tracing", - "tracing-subscriber", - "uuid", - "web-prover-core", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3907,6 +3834,36 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +[[package]] +name = "web-prover-client" +version = "0.7.0" +dependencies = [ + "base64 0.22.1", + "bytes", + "clap", + "futures", + "hex", + "http-body-util", + "hyper", + "hyper-util", + "reqwest", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "tracing-subscriber", + "url", + "uuid", + "web-prover-core", + "webpki-roots", +] + [[package]] name = "web-prover-core" version = "0.7.0" @@ -3922,6 +3879,49 @@ dependencies = [ "url", ] +[[package]] +name = "web-prover-notary" +version = "0.7.0" +dependencies = [ + "alloy-primitives", + "async-trait", + "axum", + "axum-core", + "base64 0.21.7", + "chrono", + "clap", + "config", + "eyre", + "futures", + "futures-util", + "hex", + "http", + "hyper", + "hyper-util", + "k256", + "nom", + "reqwest", + "rs_merkle", + "rustls", + "rustls-acme", + "rustls-pemfile", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-util", + "tower 0.4.13", + "tower-http", + "tower-service", + "tracing", + "tracing-subscriber", + "uuid", + "web-prover-client", + "web-prover-core", +] + [[package]] name = "web-prover-tests" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 25a1a0248..f87e1aaa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,24 @@ [workspace.package] -name ="webprover" edition="2021" +name ="webprover" [workspace] -members =["client", "notary", "web-prover-core", "tests"] +members =["client", "notary", "core", "tests"] resolver="2" [workspace.dependencies] # Local re-exporting -client ={ path="client" } -notary ={ path="notary" } -web-prover-core={ path="web-prover-core" } +web-prover-client={ path="client" } +web-prover-core ={ path="core" } +web-prover-notary={ path="notary" } # Serde serde ={ version="1.0.204", features=["derive"] } serde_json="1.0.120" # Logging +futures ="0.3" +rayon ="1.10.0" tracing ="0.1.40" tracing-subscriber={ version="0.3.18", features=["env-filter"] } -rayon ="1.10.0" -futures ="0.3" # CLI clap={ version="4.5.13", features=["derive"] } @@ -26,31 +26,31 @@ clap={ version="4.5.13", features=["derive"] } thiserror="1.0.61" # HTTP +http-body-util="0.1" hyper ={ version="1.6", features=["full"] } hyper-util ={ version="0.1", features=["full"] } -http-body-util="0.1" # Async tokio ={ version="1.39.1", features=["full"] } -tokio-util ={ version="0.7" } tokio-rustls={ version="0.26.0", default-features=false, features=["logging", "tls12"] } +tokio-util ={ version="0.7" } -uuid ={ version="1.10.0", default-features=false, features=["v4", "serde"] } +chrono ="0.4" derive_more={ version="2.0.1", features=["full"] } url ="2.5.4" -chrono ="0.4" +uuid ={ version="1.10.0", default-features=false, features=["v4", "serde"] } tracing-test="0.2" [profile.dev] +incremental =true opt-level =1 split-debuginfo="unpacked" -incremental =true [profile.release] -opt-level =0 -lto =false codegen-units=1 +debug =true # Propagate more information up through FFI +lto =false +opt-level =0 panic ="abort" strip =true -debug =true # Propagate more information up through FFI diff --git a/book/book.toml b/book/book.toml index 4e472e3f5..12cb9e7da 100644 --- a/book/book.toml +++ b/book/book.toml @@ -1,15 +1,15 @@ [book] authors =["Pluto"] +description ="Backend for Web Proofs" language ="en" multilingual=false src ="." title ="Web Prover" -description ="Backend for Web Proofs" [build] build-dir ="." -extra-watch-dirs =[] # Don't watch any extra directories create-missing =false # Don't create missing files +extra-watch-dirs =[] # Don't watch any extra directories use-default-preprocessors=false [preprocessor.links] @@ -29,8 +29,8 @@ warning-policy ="ignore" [output.html] default-theme ="dark" -preferred-dark-theme="ayu" git-repository-url ="https://github.com/pluto/web-prover" +preferred-dark-theme="ayu" # [output.html.playground] # editable=true diff --git a/client/Cargo.toml b/client/Cargo.toml index bc740bd77..3f908ddeb 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,35 +1,35 @@ [package] -name ="client" -version="0.7.0" -edition="2021" build ="build.rs" +edition="2021" +name ="web-prover-client" publish=false +version="0.7.0" [features] default =[] -websocket=[] tracing =[] +websocket=[] # used to disable cert verification, useful for development unsafe_skip_cert_verification=[] # Shared dependencies for all targets [dependencies] -web-prover-core={ workspace=true } bytes ="1" -webpki-roots ="0.26.1" pki-types ={ package="rustls-pki-types", version="1.7" } +web-prover-core={ workspace=true } +webpki-roots ="0.26.1" # Serde serde ={ workspace=true } serde_json={ workspace=true } # Web hex ="0.4" -url ="2.5" -hyper ={ workspace=true, features=["client", "http1"] } http-body-util={ workspace=true } +hyper ={ workspace=true, features=["client", "http1"] } +url ="2.5" # Logging and errors +thiserror ={ workspace=true } tracing ={ workspace=true } tracing-subscriber={ workspace=true } -thiserror ={ workspace=true } # Async futures={ workspace=true } # Other @@ -38,15 +38,15 @@ tokio-util={ version="0.7", features=[ "compat", ] } # compat is used to work with AsyncRead and AsyncWrite from other crates -uuid ={ workspace=true } clap ={ workspace=true } serde_with={ version="3.12.0", features=["base64"] } +uuid ={ workspace=true } # Web hyper-util={ workspace=true } # Async -tokio-rustls={ version="0.26", default-features=false, features=["logging", "tls12"] } -tokio ={ workspace=true, features=["rt", "rt-multi-thread", "macros", "net", "io-std", "fs"] } rustls ={ version="0.23", default-features=false, features=["ring"] } +tokio ={ workspace=true, features=["rt", "rt-multi-thread", "macros", "net", "io-std", "fs"] } +tokio-rustls={ version="0.26", default-features=false, features=["logging", "tls12"] } # TLSN reqwest={ version="0.12", features=["json", "rustls-tls"] } diff --git a/web-prover-core/Cargo.toml b/core/Cargo.toml similarity index 100% rename from web-prover-core/Cargo.toml rename to core/Cargo.toml index 329b11243..bc8c47611 100644 --- a/web-prover-core/Cargo.toml +++ b/core/Cargo.toml @@ -1,16 +1,16 @@ [package] +edition="2021" name ="web-prover-core" version="0.7.0" -edition="2021" [dependencies] +derive_more={ workspace=true } serde ={ workspace=true } serde_json ={ workspace=true } -tracing ={ workspace=true } thiserror ={ workspace=true } tiny-keccak={ version="2.0.2", features=["keccak"] } +tracing ={ workspace=true } url ={ workspace=true } -derive_more={ workspace=true } # Using `regress` crate for compatibility with ECMAScript regular expressions in Manifest validation -regress="0.10.3" regex ="1.11.1" +regress="0.10.3" diff --git a/web-prover-core/src/errors.rs b/core/src/errors.rs similarity index 100% rename from web-prover-core/src/errors.rs rename to core/src/errors.rs diff --git a/web-prover-core/src/http.rs b/core/src/http.rs similarity index 100% rename from web-prover-core/src/http.rs rename to core/src/http.rs diff --git a/web-prover-core/src/lib.rs b/core/src/lib.rs similarity index 100% rename from web-prover-core/src/lib.rs rename to core/src/lib.rs diff --git a/web-prover-core/src/manifest.rs b/core/src/manifest.rs similarity index 100% rename from web-prover-core/src/manifest.rs rename to core/src/manifest.rs diff --git a/web-prover-core/src/proof.rs b/core/src/proof.rs similarity index 100% rename from web-prover-core/src/proof.rs rename to core/src/proof.rs diff --git a/web-prover-core/src/test_utils.rs b/core/src/test_utils.rs similarity index 100% rename from web-prover-core/src/test_utils.rs rename to core/src/test_utils.rs diff --git a/fixture/notary-config.toml b/fixture/notary-config.toml index 8c29a01f4..0d9846751 100644 --- a/fixture/notary-config.toml +++ b/fixture/notary-config.toml @@ -1,4 +1,4 @@ listen ="0.0.0.0:7443" +notary_signing_key="./fixture/certs/notary.key" server_cert ="./fixture/certs/server-cert.pem" server_key ="./fixture/certs/server-key.pem" -notary_signing_key="./fixture/certs/notary.key" \ No newline at end of file diff --git a/notary/Cargo.toml b/notary/Cargo.toml index d7b4964ce..77e62e3d8 100644 --- a/notary/Cargo.toml +++ b/notary/Cargo.toml @@ -1,46 +1,46 @@ [package] -name ="notary" -version="0.7.0" -edition="2021" build ="build.rs" +edition="2021" +name ="web-prover-notary" +version="0.7.0" [dependencies] -web-prover-core ={ workspace=true } chrono ={ workspace=true } -client ={ workspace=true } +futures ={ workspace=true } +futures-util ="0.3.30" hyper ={ workspace=true, features=["client", "http1", "server"] } hyper-util ={ workspace=true } +nom ="7.0" +rustls ={ version="0.23.11", default-features=false, features=["logging", "tls12", "std", "ring"] } +rustls-pemfile ="2.1.2" serde ={ workspace=true } serde_json ={ workspace=true } -rustls ={ version="0.23.11", default-features=false, features=["logging", "tls12", "std", "ring"] } tokio ={ workspace=true } -tokio-util ={ workspace=true, features=["compat"] } -tracing ={ workspace=true } -tracing-subscriber={ workspace=true } -futures ={ workspace=true } -futures-util ="0.3.30" tokio-rustls ={ workspace=true, features=["ring"] } +tokio-util ={ workspace=true, features=["compat"] } tower-http ={ version="0.5.2", features=["cors"] } tower-service ="0.3.2" -rustls-pemfile ="2.1.2" -nom ="7.0" +tracing ={ workspace=true } +tracing-subscriber={ workspace=true } +web-prover-client ={ workspace=true } +web-prover-core ={ workspace=true } +alloy-primitives={ version="0.8.2", features=["k256"] } async-trait ="0.1.67" axum ={ version="0.7", features=["ws", "json"] } axum-core ="0.4" -eyre ="0.6.8" base64 ="0.21.0" -http ="1.1" -config ="0.14.0" clap ={ workspace=true } -rustls-acme ={ version="0.10", default-features=false, features=["ring", "tokio"] } -tokio-stream ={ version="0.1", features=["net"] } -thiserror ={ workspace=true } +config ="0.14.0" +eyre ="0.6.8" hex ="0.4" -rs_merkle ="1.4.2" -alloy-primitives={ version="0.8.2", features=["k256"] } +http ="1.1" k256 ={ version="0.13.3", features=["ecdsa", "pem"] } reqwest ={ version="0.12", features=["json"] } +rs_merkle ="1.4.2" +rustls-acme ={ version="0.10", default-features=false, features=["ring", "tokio"] } +thiserror ={ workspace=true } +tokio-stream ={ version="0.1", features=["net"] } uuid ={ workspace=true } [dev-dependencies] diff --git a/taplo.toml b/taplo.toml index f58bb47b4..39051ed0c 100644 --- a/taplo.toml +++ b/taplo.toml @@ -13,7 +13,7 @@ column_width=100 # remove whitespace around '=' compact_entries=true # alphabetically sort entries not separated by line breaks -reorder_keys=false +reorder_keys=true # align entries vertically (default: true) # align_comments =false # expand arrays into multiple lines (default: true) diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 547249b65..3efc8cfe6 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,9 +1,9 @@ [package] +edition="2021" name ="web-prover-tests" version="0.1.0" -edition="2021" [dependencies] +serde_json={ workspace=true } tokio ={ workspace=true } tracing ={ workspace=true } -serde_json={ workspace=true } From 87b9978a1de82c2a9248bacf0168265218745df3 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:38:56 -0700 Subject: [PATCH 03/22] Update lint.yaml --- .github/workflows/lint.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 987547d38..f8592761b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -11,8 +11,9 @@ jobs: fail-fast: false matrix: crate: - - notary - - client + - web-prover-notary + - web-prover-client + - web-prover-core steps: - uses: actions/checkout@v4 From 74d1005408bddd4d066b6e95072c8975192fd02a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:48:39 -0700 Subject: [PATCH 04/22] fix: build --- .github/workflows/build_client_native.yaml | 10 +++++----- .github/workflows/build_notary.yaml | 4 ++-- .github/workflows/release.yaml | 4 ++-- client/src/main.rs | 4 ++-- notary/src/proxy.rs | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build_client_native.yaml b/.github/workflows/build_client_native.yaml index 27fe16b9d..f7b1f24d2 100644 --- a/.github/workflows/build_client_native.yaml +++ b/.github/workflows/build_client_native.yaml @@ -27,20 +27,20 @@ jobs: rust-cache-key: client_native - run: | - cargo build -p client --release + cargo build -p web-prover-client --release - uses: actions/upload-artifact@v4 if: matrix.os == 'ubuntu-latest' with: - name: "client.linux.amd64" - path: "target/release/client" + name: "web-prover-client.linux.amd64" + path: "target/release/web-prover-client" retention-days: 7 if-no-files-found: "error" - uses: actions/upload-artifact@v4 if: matrix.os == 'macos-latest' with: - name: "client.macos.arm64" - path: "target/release/client" + name: "web-prover-client.macos.arm64" + path: "target/release/web-prover-client" retention-days: 7 if-no-files-found: "error" diff --git a/.github/workflows/build_notary.yaml b/.github/workflows/build_notary.yaml index 394b84644..ac5e6d9a8 100644 --- a/.github/workflows/build_notary.yaml +++ b/.github/workflows/build_notary.yaml @@ -26,12 +26,12 @@ jobs: with: rust-cache-key: client_notary - - run: cargo build -p notary --release + - run: cargo build -p web-prover-notary --release - uses: actions/upload-artifact@v4 if: matrix.os == 'ubuntu-latest' with: name: "notary.linux.amd64" - path: "target/release/notary" + path: "target/release/web-prover-notary" retention-days: 7 if-no-files-found: "error" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fbd58bd21..2116c0c87 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -27,8 +27,8 @@ jobs: shell: bash run: | mkdir -p releases/clients - cp target/release/client.linux.amd64/client releases/clients/client.linux.amd64 - cp target/release/client.macos.arm64/client releases/clients/client.macos.arm64 + cp target/release/web-prover-client.linux.amd64/web-prover-client releases/clients/client.linux.amd64 + cp target/release/web-prover-client.macos.arm64/web-prover-client releases/clients/client.macos.arm64 cd releases/clients tar -czf client.linux.amd64.tar.gz client.linux.amd64 rm client.linux.amd64 diff --git a/client/src/main.rs b/client/src/main.rs index 471fabad5..3dd4f4a3f 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; -use client::{config::Config, errors::ClientErrors}; use tracing::Level; +use web_prover_client::{config::Config, errors::ClientErrors}; #[derive(Parser)] #[clap(name = "Web Proof Client")] @@ -32,7 +32,7 @@ async fn main() -> Result<(), ClientErrors> { let mut config: Config = serde_json::from_str(&config_json)?; config.set_session_id(); - let proof = client::proxy(config).await?; + let proof = web_prover_client::proxy(config).await?; let proof_json = serde_json::to_string_pretty(&proof)?; println!("Proving Successful: proof_len={:?}", proof_json.len()); Ok(()) diff --git a/notary/src/proxy.rs b/notary/src/proxy.rs index be60140f6..d7dc1f95f 100644 --- a/notary/src/proxy.rs +++ b/notary/src/proxy.rs @@ -31,7 +31,7 @@ pub struct NotarizeQuery { pub async fn proxy( query: Query, State(state): State>, - extract::Json(payload): extract::Json, + extract::Json(payload): extract::Json, ) -> Result, NotaryServerError> { let session_id = query.session_id.clone(); From 766c21ab248894c8c7d5c2d156e9eeec6f085fc6 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:54:12 -0700 Subject: [PATCH 05/22] refactor: error naming --- client/src/config.rs | 12 ++-- client/src/{errors.rs => error.rs} | 12 +++- client/src/lib.rs | 8 +-- client/src/main.rs | 4 +- core/src/{errors.rs => error.rs} | 2 +- core/src/http.rs | 101 +++++++++++++++-------------- core/src/lib.rs | 2 +- core/src/manifest.rs | 8 +-- notary/src/{errors.rs => error.rs} | 9 ++- 9 files changed, 88 insertions(+), 70 deletions(-) rename client/src/{errors.rs => error.rs} (75%) rename core/src/{errors.rs => error.rs} (92%) rename notary/src/{errors.rs => error.rs} (78%) diff --git a/client/src/config.rs b/client/src/config.rs index 4f751f482..b9b81a42b 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -9,7 +9,7 @@ use serde_with::{ use url::Url; use web_prover_core::manifest::Manifest; -use crate::errors::ClientErrors; +use crate::error::WebProverClientError; /// Proving data containing [`Manifest`] and serialized witnesses used for WASM #[derive(Deserialize, Clone, Debug)] @@ -60,11 +60,11 @@ impl Config { /// /// # Errors /// - Returns `ClientErrors::Other` if the host is not found in the target URL. - pub fn target_host(&self) -> Result { + pub fn target_host(&self) -> Result { let target_url = Url::parse(&self.target_url)?; let host = target_url .host_str() - .ok_or_else(|| ClientErrors::Other("Host not found in target URL".to_owned()))? + .ok_or_else(|| WebProverClientError::Other("Host not found in target URL".to_owned()))? .to_string(); Ok(host) } @@ -81,11 +81,11 @@ impl Config { /// /// # Errors /// - Returns `ClientErrors::Other` if the port is not found in the target URL. - pub fn target_port(&self) -> Result { + pub fn target_port(&self) -> Result { let target_url = Url::parse(&self.target_url)?; let port = target_url .port_or_known_default() - .ok_or_else(|| ClientErrors::Other("Port not found in target URL".to_owned()))?; + .ok_or_else(|| WebProverClientError::Other("Port not found in target URL".to_owned()))?; Ok(port) } @@ -99,7 +99,7 @@ impl Config { /// /// # Errors /// - Returns `ClientErrors::Other` if the URL is invalid. - pub fn target_is_https(&self) -> Result { + pub fn target_is_https(&self) -> Result { let target_url = Url::parse(&self.target_url)?; Ok(target_url.scheme() == "https") } diff --git a/client/src/errors.rs b/client/src/error.rs similarity index 75% rename from client/src/errors.rs rename to client/src/error.rs index 4c060d69c..d536a75a0 100644 --- a/client/src/errors.rs +++ b/client/src/error.rs @@ -1,12 +1,18 @@ use std::array::TryFromSliceError; use thiserror::Error; -impl From for ClientErrors { - fn from(err: TryFromSliceError) -> ClientErrors { ClientErrors::Other(err.to_string()) } +impl From for WebProverClientError { + fn from(err: TryFromSliceError) -> WebProverClientError { + WebProverClientError::Other(err.to_string()) + } } +// TODO (autoparallel): Combining enums is a good practice. This error enum could also all be moved +// to the `web-prover-core` crate so there is one spot for all the errors. This makes error handling +// more consistent and easier to manage. + #[derive(Debug, Error)] -pub enum ClientErrors { +pub enum WebProverClientError { #[cfg(not(target_arch = "wasm32"))] #[error(transparent)] RustTls(#[from] rustls::Error), diff --git a/client/src/lib.rs b/client/src/lib.rs index 8e087e5a5..f7472eb40 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,6 +1,6 @@ extern crate core; pub mod config; -pub mod errors; +pub mod error; use std::collections::HashMap; use serde::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ use web_prover_core::{ proof::{SignedVerificationReply, TeeProof}, }; -use crate::errors::ClientErrors; +use crate::error::WebProverClientError; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ProxyConfig { @@ -21,7 +21,7 @@ pub struct ProxyConfig { pub manifest: Manifest, } -pub async fn proxy(config: config::Config) -> Result { +pub async fn proxy(config: config::Config) -> Result { let session_id = config.session_id.clone(); let url = format!( @@ -57,7 +57,7 @@ pub async fn proxy(config: config::Config) -> Result { pub async fn verify( config: crate::config::Config, verify_body: T, -) -> Result { +) -> Result { let url = format!( "https://{}:{}/v1/{}/verify", config.notary_host.clone(), diff --git a/client/src/main.rs b/client/src/main.rs index 3dd4f4a3f..9c828f96b 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; use tracing::Level; -use web_prover_client::{config::Config, errors::ClientErrors}; +use web_prover_client::{config::Config, error::WebProverClientError}; #[derive(Parser)] #[clap(name = "Web Proof Client")] @@ -14,7 +14,7 @@ struct Args { } #[tokio::main] -async fn main() -> Result<(), ClientErrors> { +async fn main() -> Result<(), WebProverClientError> { let args = Args::parse(); let log_level = match args.log_level.to_lowercase().as_str() { diff --git a/core/src/errors.rs b/core/src/error.rs similarity index 92% rename from core/src/errors.rs rename to core/src/error.rs index 6e69c2fb9..6c636e37b 100644 --- a/core/src/errors.rs +++ b/core/src/error.rs @@ -4,7 +4,7 @@ use thiserror::Error; /// Represents the various error conditions that can occur within the `proofs` crate. #[derive(Debug, Error)] -pub enum ManifestError { +pub enum WebProverCoreError { /// The error is an invalid manifest #[error("Invalid manifest: {0}")] InvalidManifest(String), diff --git a/core/src/http.rs b/core/src/http.rs index e6a85dce1..a539b9563 100644 --- a/core/src/http.rs +++ b/core/src/http.rs @@ -27,7 +27,7 @@ pub enum JsonKey { Num(usize), } -use crate::errors::ManifestError; +use crate::error::WebProverCoreError; /// Max HTTP headers pub const MAX_HTTP_HEADERS: usize = 25; @@ -43,7 +43,7 @@ pub struct ManifestResponseBody { } impl TryFrom<&[u8]> for ManifestResponseBody { - type Error = ManifestError; + type Error = WebProverCoreError; fn try_from(body_bytes: &[u8]) -> Result { if body_bytes.is_empty() { @@ -51,7 +51,7 @@ impl TryFrom<&[u8]> for ManifestResponseBody { } // Attempt to parse the body as JSON path. let json_path: Vec = serde_json::from_slice(body_bytes).map_err(|_| { - ManifestError::InvalidManifest("Failed to parse body as valid JSON".to_string()) + WebProverCoreError::InvalidManifest("Failed to parse body as valid JSON".to_string()) })?; Ok(Self { json_path }) } @@ -66,7 +66,7 @@ pub struct NotaryResponseBody { } impl TryFrom<&[u8]> for NotaryResponseBody { - type Error = ManifestError; + type Error = WebProverCoreError; fn try_from(body_bytes: &[u8]) -> Result { if body_bytes.is_empty() { @@ -74,7 +74,7 @@ impl TryFrom<&[u8]> for NotaryResponseBody { } // Attempt to parse the body as JSON. let json: serde_json::Value = serde_json::from_slice(body_bytes).map_err(|_| { - ManifestError::InvalidManifest("Failed to parse body as valid JSON".to_string()) + WebProverCoreError::InvalidManifest("Failed to parse body as valid JSON".to_string()) })?; Ok(Self { json: Some(json) }) } @@ -201,12 +201,12 @@ impl NotaryResponse { /// /// - `header_bytes`: The bytes representing the HTTP response headers and metadata. /// - `body_bytes`: The bytes representing the HTTP response body. - pub fn from_payload(bytes: &[u8]) -> Result { + pub fn from_payload(bytes: &[u8]) -> Result { let delimiter = b"\r\n\r\n"; let split_position = bytes .windows(delimiter.len()) .position(|window| window == delimiter) - .ok_or_else(|| ManifestError::InvalidManifest("Invalid HTTP format".to_string()))?; + .ok_or_else(|| WebProverCoreError::InvalidManifest("Invalid HTTP format".to_string()))?; let (header_bytes, rest) = bytes.split_at(split_position); let body_bytes = &rest[delimiter.len()..]; @@ -230,9 +230,9 @@ impl NotaryResponse { /// The parsed HTTP response header. fn parse_header( header_bytes: &[u8], - ) -> Result<(HashMap, String, String, String), ManifestError> { + ) -> Result<(HashMap, String, String, String), WebProverCoreError> { let headers_str = std::str::from_utf8(header_bytes).map_err(|_| { - ManifestError::InvalidManifest("Failed to interpret headers as valid UTF-8".to_string()) + WebProverCoreError::InvalidManifest("Failed to interpret headers as valid UTF-8".to_string()) })?; let mut headers = HashMap::new(); let mut status = String::new(); @@ -247,7 +247,7 @@ impl NotaryResponse { // Process the first line as the HTTP response start-line let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() < 3 { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid HTTP response start-line".to_string(), )); } @@ -259,7 +259,10 @@ impl NotaryResponse { if let Some((key, value)) = line.split_once(": ") { headers.insert(key.to_string(), value.to_string()); } else { - return Err(ManifestError::InvalidManifest(format!("Invalid header line: {}", line))); + return Err(WebProverCoreError::InvalidManifest(format!( + "Invalid header line: {}", + line + ))); } } } @@ -346,16 +349,16 @@ impl ManifestResponse { /// # Returns /// /// The validated HTTP response. - pub fn validate(&self) -> Result<(), ManifestError> { + pub fn validate(&self) -> Result<(), WebProverCoreError> { // TODO: What are legal statuses? const VALID_STATUSES: [&str; 2] = ["200", "201"]; if !VALID_STATUSES.contains(&self.status.as_str()) { - return Err(ManifestError::InvalidManifest("Unsupported HTTP status".to_string())); + return Err(WebProverCoreError::InvalidManifest("Unsupported HTTP status".to_string())); } // TODO: What HTTP versions are supported? if self.version != "HTTP/1.1" { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid HTTP version: ".to_string() + &self.version, )); } @@ -363,14 +366,14 @@ impl ManifestResponse { // TODO: What is the max supported message length? // TODO: Not covered by serde's #default annotation. Is '""' a valid message? if self.message.len() > 1024 || self.message.is_empty() { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid message length: ".to_string() + &self.message, )); } // We always expect at least one header, "Content-Type" if self.headers.len() > MAX_HTTP_HEADERS || self.headers.is_empty() { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid headers length: ".to_string() + &self.headers.len().to_string(), )); } @@ -378,7 +381,7 @@ impl ManifestResponse { let content_type = self.headers.get("Content-Type").or_else(|| self.headers.get("content-type")); if content_type.is_none() { - return Err(ManifestError::InvalidManifest("Missing 'Content-Type' header".to_string())); + return Err(WebProverCoreError::InvalidManifest("Missing 'Content-Type' header".to_string())); } let content_type = content_type.unwrap(); @@ -387,19 +390,21 @@ impl ManifestResponse { content_type == legal_type || content_type.starts_with(&format!("{};", legal_type)) }); if !is_valid_content_type { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid Content-Type header: ".to_string() + content_type, )); } // When Content-Type is application/json, we expect at least one JSON item if content_type == "application/json" && self.body.json_path.is_empty() { - return Err(ManifestError::InvalidManifest("Expected at least one JSON item".to_string())); + return Err(WebProverCoreError::InvalidManifest( + "Expected at least one JSON item".to_string(), + )); } const MAX_JSON_PATH_LENGTH: usize = 100; if self.body.json_path.len() > MAX_JSON_PATH_LENGTH { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid JSON path length: ".to_string() + &self.body.json_path.len().to_string(), )); } @@ -448,25 +453,25 @@ impl ManifestRequest { /// # Returns /// /// The validated HTTP request. - pub fn validate(&self) -> Result<(), ManifestError> { + pub fn validate(&self) -> Result<(), WebProverCoreError> { // TODO: What HTTP methods are supported? const ALLOWED_METHODS: [&str; 2] = ["GET", "POST"]; if !ALLOWED_METHODS.contains(&self.method.as_str()) { - return Err(ManifestError::InvalidManifest("Invalid HTTP method".to_string())); + return Err(WebProverCoreError::InvalidManifest("Invalid HTTP method".to_string())); } // Not a valid URL if url::Url::parse(&self.url).is_err() { - return Err(ManifestError::InvalidManifest("Invalid URL: ".to_string() + &self.url)); + return Err(WebProverCoreError::InvalidManifest("Invalid URL: ".to_string() + &self.url)); } if !self.url.starts_with("https://") { - return Err(ManifestError::InvalidManifest("Only HTTPS URLs are allowed".to_string())); + return Err(WebProverCoreError::InvalidManifest("Only HTTPS URLs are allowed".to_string())); } // TODO: What HTTP versions are supported? if self.version != "HTTP/1.1" { - return Err(ManifestError::InvalidManifest( + return Err(WebProverCoreError::InvalidManifest( "Invalid HTTP version: ".to_string() + &self.version, )); } @@ -474,14 +479,14 @@ impl ManifestRequest { Ok(()) } - fn validate_vars(&self) -> Result<(), ManifestError> { + fn validate_vars(&self) -> Result<(), WebProverCoreError> { let mut all_tokens = vec![]; // Parse and validate tokens in the body if let Some(body_tokens) = self.body.as_ref().map(extract_tokens) { for token in &body_tokens { if !self.vars.contains_key(token) { - return Err(ManifestError::InvalidManifest(format!( + return Err(WebProverCoreError::InvalidManifest(format!( "Token `<% {} %>` not declared in `vars`", token ))); @@ -495,7 +500,7 @@ impl ManifestRequest { let header_tokens = extract_tokens(&serde_json::Value::String(value.clone())); for token in &header_tokens { if !self.vars.contains_key(token) { - return Err(ManifestError::InvalidManifest(format!( + return Err(WebProverCoreError::InvalidManifest(format!( "Token `<% {} %>` not declared in `vars`", token ))); @@ -506,7 +511,7 @@ impl ManifestRequest { for var_key in self.vars.keys() { if !all_tokens.contains(var_key) { - return Err(ManifestError::InvalidManifest(format!( + return Err(WebProverCoreError::InvalidManifest(format!( "Token `<% {} %>` not declared in `body` or `headers`", var_key ))); @@ -519,7 +524,7 @@ impl ManifestRequest { if let Some(regex_pattern) = var_def.regex.as_ref() { // Using `regress` crate for compatibility with ECMAScript regular expressions let _regex = regress::Regex::new(regex_pattern).map_err(|_| { - ManifestError::InvalidManifest(format!("Invalid regex pattern for `{}`", key)) + WebProverCoreError::InvalidManifest(format!("Invalid regex pattern for `{}`", key)) })?; // TODO: It will definitely not match it here because it's a template variable, not an // actual variable @@ -538,7 +543,7 @@ impl ManifestRequest { if let Some(length) = var_def.length { if let Some(value) = self.body.as_ref().and_then(|b| b.get(key)) { if value.as_str().unwrap_or("").len() != length { - return Err(ManifestError::InvalidManifest(format!( + return Err(WebProverCoreError::InvalidManifest(format!( "Value for token `<% {} %>` does not meet length constraint", key ))); @@ -552,13 +557,13 @@ impl ManifestRequest { } /// Parses the HTTP request from the given bytes. - pub fn from_payload(bytes: &[u8]) -> Result { + pub fn from_payload(bytes: &[u8]) -> Result { // todo: dedup me let delimiter = b"\r\n\r\n"; let split_position = bytes .windows(delimiter.len()) .position(|window| window == delimiter) - .ok_or_else(|| ManifestError::InvalidManifest("Invalid HTTP format".to_string()))?; + .ok_or_else(|| WebProverCoreError::InvalidManifest("Invalid HTTP format".to_string()))?; let (header_bytes, rest) = bytes.split_at(split_position); let body_bytes = &rest[delimiter.len()..]; @@ -567,7 +572,7 @@ impl ManifestRequest { let body = if !body_bytes.is_empty() { serde_json::from_slice(body_bytes) - .map_err(|_| ManifestError::InvalidManifest("Invalid body bytes".to_string()))? + .map_err(|_| WebProverCoreError::InvalidManifest("Invalid body bytes".to_string()))? } else { None }; @@ -578,19 +583,21 @@ impl ManifestRequest { /// Parses the HTTP request start-line and headers from the given bytes. fn parse_header( header_bytes: &[u8], - ) -> Result<(String, String, String, HashMap), ManifestError> { + ) -> Result<(String, String, String, HashMap), WebProverCoreError> { let header_str = std::str::from_utf8(header_bytes).map_err(|_| { - ManifestError::InvalidManifest("Failed to interpret headers as valid UTF-8".to_string()) + WebProverCoreError::InvalidManifest("Failed to interpret headers as valid UTF-8".to_string()) })?; let mut lines = header_str.lines(); let start_line = lines.next().ok_or_else(|| { - ManifestError::InvalidManifest("Missing start-line in the HTTP request.".to_string()) + WebProverCoreError::InvalidManifest("Missing start-line in the HTTP request.".to_string()) })?; let parts: Vec<&str> = start_line.split_whitespace().collect(); if parts.len() < 3 { - return Err(ManifestError::InvalidManifest("Invalid HTTP request start-line.".to_string())); + return Err(WebProverCoreError::InvalidManifest( + "Invalid HTTP request start-line.".to_string(), + )); } let method = parts[0].to_string(); @@ -605,7 +612,7 @@ impl ManifestRequest { if let Some((key, value)) = line.split_once(": ") { headers.insert(key.to_string(), value.to_string()); } else { - return Err(ManifestError::InvalidManifest(format!("Invalid header line: {}", line))); + return Err(WebProverCoreError::InvalidManifest(format!("Invalid header line: {}", line))); } } @@ -846,7 +853,7 @@ pub mod tests { let result = NotaryResponse::from_payload(&[header_bytes, invalid_body_bytes].concat()); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(msg)) = result { + if let Err(WebProverCoreError::InvalidManifest(msg)) = result { assert!(msg.contains("Failed to parse body as valid JSON")); } else { panic!("Expected an invalid manifest error for body parsing"); @@ -864,7 +871,7 @@ pub mod tests { assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Unsupported HTTP status")); }, _ => panic!("Expected invalid manifest error for unsupported HTTP status"), @@ -879,7 +886,7 @@ pub mod tests { let result = invalid_response.validate(); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(msg)) = result { + if let Err(WebProverCoreError::InvalidManifest(msg)) = result { assert!(msg.contains("Invalid message length")); } else { panic!("Expected invalid manifest error for empty message"); @@ -966,7 +973,7 @@ pub mod tests { assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Failed to interpret headers as valid UTF-8")); }, _ => panic!("Expected invalid UTF-8 headers error"), @@ -982,7 +989,7 @@ pub mod tests { assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Invalid HTTP request start-line")); }, _ => panic!("Expected invalid start-line error"), @@ -999,7 +1006,7 @@ pub mod tests { let result = request.validate(); assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Only HTTPS URLs are allowed")); }, _ => panic!("Expected error for non-HTTPS URL"), @@ -1026,7 +1033,7 @@ pub mod tests { assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Invalid body bytes")); }, _ => panic!("Expected invalid body parsing error"), @@ -1046,7 +1053,7 @@ pub mod tests { assert!(result.is_err()); match result { - Err(ManifestError::InvalidManifest(msg)) => { + Err(WebProverCoreError::InvalidManifest(msg)) => { assert!(msg.contains("Token `<% missing_token %>` not declared in `vars`")); }, _ => panic!("Expected missing token error"), diff --git a/core/src/lib.rs b/core/src/lib.rs index bf4b92c4d..e5005aa04 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,4 +1,4 @@ -pub mod errors; +pub mod error; pub mod http; pub mod manifest; diff --git a/core/src/manifest.rs b/core/src/manifest.rs index efb631b0c..eaefdc4a6 100644 --- a/core/src/manifest.rs +++ b/core/src/manifest.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use tiny_keccak::{Hasher, Keccak}; use crate::{ - errors::ManifestError, + error::WebProverCoreError, http::{ManifestRequest, ManifestResponse}, }; @@ -28,7 +28,7 @@ pub struct Manifest { impl Manifest { /// Validates `Manifest` request and response fields. They are validated against valid statuses, /// http methods, and template variables. - pub fn validate(&self) -> Result<(), ManifestError> { + pub fn validate(&self) -> Result<(), WebProverCoreError> { // TODO: Validate manifest version, id, title, description, prepareUrl self.request.validate()?; self.response.validate()?; @@ -42,7 +42,7 @@ impl Manifest { } /// Compute a `Keccak256` hash of the serialized Manifest - pub fn to_keccak_digest(&self) -> Result<[u8; 32], ManifestError> { + pub fn to_keccak_digest(&self) -> Result<[u8; 32], WebProverCoreError> { let as_bytes: Vec = self.try_into()?; let mut hasher = Keccak::v256(); let mut output = [0u8; 32]; @@ -77,7 +77,7 @@ mod tests { use std::collections::HashMap; use crate::{ - errors::ProofError, + error::ProofError, program::{ http::{JsonKey, ManifestRequest, ManifestResponse, ManifestResponseBody, TemplateVar}, manifest::HTTP_1_1, diff --git a/notary/src/errors.rs b/notary/src/error.rs similarity index 78% rename from notary/src/errors.rs rename to notary/src/error.rs index d68bdfc76..536318c8d 100644 --- a/notary/src/errors.rs +++ b/notary/src/error.rs @@ -5,7 +5,12 @@ use axum::{ use eyre::Report; use thiserror::Error; use tracing::error; -use web_prover_core::errors::ManifestError; +use web_prover_core::error::WebProverCoreError; + +// TODO (autoparallel): I think these error enums should be combined into `WebProverNotaryError`. +// Combining enums is a good practice. They could also all be moved to the `web-prover-core` crate +// so there is one spot for all the errors. This makes error handling more consistent and easier to +// manage. #[derive(Debug, Error)] pub enum ProxyError { @@ -51,7 +56,7 @@ pub enum NotaryServerError { ProxyError(#[from] ProxyError), #[error(transparent)] - ManifestError(#[from] ManifestError), + ManifestError(#[from] WebProverCoreError), } /// Trait implementation to convert this error into an axum http response From 3a408d324e1e76298bef076462ae5e9003b9b237 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:55:23 -0700 Subject: [PATCH 06/22] Update main.rs --- notary/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notary/src/main.rs b/notary/src/main.rs index 396293e40..e4b93616f 100644 --- a/notary/src/main.rs +++ b/notary/src/main.rs @@ -12,7 +12,7 @@ use axum::{ routing::{get, post}, Router, }; -use errors::NotaryServerError; +use error::NotaryServerError; use hyper::{body::Incoming, server::conn::http1}; use hyper_util::rt::TokioIo; use k256::{ecdsa::SigningKey, elliptic_curve::rand_core, pkcs8::DecodePrivateKey}; @@ -30,7 +30,7 @@ use tracing::{error, info}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod config; -mod errors; +mod error; mod proxy; mod verifier; From e57ba2dfeeaaf5b3e2f991fcaa7c9b5c98177c5b Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:58:55 -0700 Subject: [PATCH 07/22] fix: reference to `errors` module --- notary/src/proxy.rs | 2 +- notary/src/verifier.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/notary/src/proxy.rs b/notary/src/proxy.rs index d7dc1f95f..700acf4b1 100644 --- a/notary/src/proxy.rs +++ b/notary/src/proxy.rs @@ -18,7 +18,7 @@ use web_prover_core::{ }; use crate::{ - errors::NotaryServerError, + error::NotaryServerError, verifier::{sign_verification, VerifyOutput}, SharedState, }; diff --git a/notary/src/verifier.rs b/notary/src/verifier.rs index cca07c20b..771720e6e 100644 --- a/notary/src/verifier.rs +++ b/notary/src/verifier.rs @@ -5,7 +5,7 @@ use rs_merkle::{Hasher, MerkleTree}; use serde::{Deserialize, Serialize}; use web_prover_core::{manifest::Manifest, proof::SignedVerificationReply}; -use crate::{errors::NotaryServerError, SharedState, State}; +use crate::{error::NotaryServerError, SharedState, State}; #[derive(Clone)] struct KeccakHasher; From 131698dd4cba120e6a8d76bc964ce99fd049550a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:59:04 -0700 Subject: [PATCH 08/22] feat: `udeps` in CI --- .github/workflows/lint.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index f8592761b..82946ab90 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -36,3 +36,26 @@ jobs: - run: rustup component add rustfmt - run: cargo fmt --all -- --check + + udeps: + continue-on-error: true + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - web-prover-notary + - web-prover-client + - web-prover-core + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: udeps + + - name: Install cargo-udeps + run: cargo install cargo-udeps --locked + + - name: Check unused dependencies + run: cargo udeps -p ${{ matrix.crate }} From 52e7000da507e1ada40d908351b51b35da1ba404 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:59:41 -0700 Subject: [PATCH 09/22] Update lint.yaml --- .github/workflows/lint.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 82946ab90..91b838346 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -54,8 +54,11 @@ jobs: with: rust-cache-key: udeps + - name: Install cargo-binstall + run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-github-release.sh | bash + - name: Install cargo-udeps - run: cargo install cargo-udeps --locked + run: cargo binstall --no-confirm cargo-udeps - name: Check unused dependencies run: cargo udeps -p ${{ matrix.crate }} From b5b01b794136b264907c915987ae4197d3dbb002 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:02:00 -0700 Subject: [PATCH 10/22] fix: clippy throws on warns --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 91b838346..225f6c5c5 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -21,7 +21,7 @@ jobs: with: rust-cache-key: clippy - - run: cargo clippy -p ${{ matrix.crate }} + - run: cargo clippy -p ${{ matrix.crate }} -- -D warnings fmt: continue-on-error: true From 13f5f7b40b735cef38ed12fe294ef007c76e9e88 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:08:13 -0700 Subject: [PATCH 11/22] fix: lint in client --- Cargo.toml | 4 ---- core/src/http.rs | 3 +++ core/src/manifest.rs | 19 +++++++++---------- notary/src/main.rs | 7 ++----- notary/src/proxy.rs | 2 +- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f87e1aaa5..00d6ad663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,3 @@ -[workspace.package] -edition="2021" -name ="webprover" - [workspace] members =["client", "notary", "core", "tests"] resolver="2" diff --git a/core/src/http.rs b/core/src/http.rs index a539b9563..a733f1b50 100644 --- a/core/src/http.rs +++ b/core/src/http.rs @@ -479,6 +479,9 @@ impl ManifestRequest { Ok(()) } + // TODO (autoparallel): This function is not used anywhere at the moment, but i did not want to + // delete it. + #[allow(unused)] fn validate_vars(&self) -> Result<(), WebProverCoreError> { let mut all_tokens = vec![]; diff --git a/core/src/manifest.rs b/core/src/manifest.rs index eaefdc4a6..5ca2e821e 100644 --- a/core/src/manifest.rs +++ b/core/src/manifest.rs @@ -77,14 +77,13 @@ mod tests { use std::collections::HashMap; use crate::{ - error::ProofError, - program::{ - http::{JsonKey, ManifestRequest, ManifestResponse, ManifestResponseBody, TemplateVar}, - manifest::HTTP_1_1, - plain_manifest::Manifest, + error::WebProverCoreError, + http::{ + JsonKey, ManifestRequest, ManifestResponse, ManifestResponseBody, TemplateVar, HTTP_1_1, }, + manifest::Manifest, request, response, - tests::inputs::TEST_MANIFEST, + test_utils::TEST_MANIFEST, }; macro_rules! create_manifest { @@ -186,7 +185,7 @@ mod tests { let manifest = create_manifest!(request!(method: "INVALID".to_string()), response!(),); let result = manifest.validate(); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(message)) = result { + if let Err(WebProverCoreError::InvalidManifest(message)) = result { assert_eq!(message, "Invalid HTTP method"); } else { panic!("Expected ManifestError::InvalidManifest"); @@ -198,7 +197,7 @@ mod tests { let manifest = create_manifest!(request!(url: "ftp://example.com".to_string()), response!(),); let result = manifest.validate(); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(message)) = result { + if let Err(WebProverCoreError::InvalidManifest(message)) = result { assert_eq!(message, "Only HTTPS URLs are allowed"); } else { panic!("Expected ManifestError::InvalidManifest"); @@ -210,7 +209,7 @@ mod tests { let manifest = create_manifest!(request!(), response!(status: "500".to_string()),); let result = manifest.validate(); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(message)) = result { + if let Err(WebProverCoreError::InvalidManifest(message)) = result { assert_eq!(message, "Unsupported HTTP status"); } else { panic!("Expected ManifestError::InvalidManifest"); @@ -247,7 +246,7 @@ mod tests { ); let result = manifest.validate(); assert!(result.is_err()); - if let Err(ManifestError::InvalidManifest(message)) = result { + if let Err(WebProverCoreError::InvalidManifest(message)) = result { assert_eq!(message, "Invalid Content-Type header: invalid/type"); } else { panic!("Expected ManifestError::InvalidManifest"); diff --git a/notary/src/main.rs b/notary/src/main.rs index e4b93616f..1df81d55b 100644 --- a/notary/src/main.rs +++ b/notary/src/main.rs @@ -36,7 +36,6 @@ mod verifier; struct SharedState { notary_signing_key: SigningKey, - sessions: Arc>>, } struct Session {} @@ -88,10 +87,8 @@ async fn main() -> Result<(), NotaryServerError> { let listener = TcpListener::bind(&c.listen).await?; info!("Listening on https://{}", &c.listen); - let shared_state = Arc::new(SharedState { - notary_signing_key: load_notary_signing_key(&c.notary_signing_key), - sessions: Default::default(), - }); + let shared_state = + Arc::new(SharedState { notary_signing_key: load_notary_signing_key(&c.notary_signing_key) }); let router = Router::new() .route("/health", get(|| async move { (StatusCode::OK, "Ok").into_response() })) diff --git a/notary/src/proxy.rs b/notary/src/proxy.rs index 700acf4b1..4853ef849 100644 --- a/notary/src/proxy.rs +++ b/notary/src/proxy.rs @@ -33,7 +33,7 @@ pub async fn proxy( State(state): State>, extract::Json(payload): extract::Json, ) -> Result, NotaryServerError> { - let session_id = query.session_id.clone(); + let session_id = query.session_id; info!("Starting proxy with ID: {}", session_id); From 00922670cea24a21287d467bdf5f3e78633bf0d6 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:08:40 -0700 Subject: [PATCH 12/22] fix: lint in notary --- notary/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/notary/src/main.rs b/notary/src/main.rs index 1df81d55b..1491f4a83 100644 --- a/notary/src/main.rs +++ b/notary/src/main.rs @@ -1,8 +1,7 @@ use std::{ - collections::HashMap, fs, io::{self}, - sync::{Arc, Mutex}, + sync::Arc, }; use axum::{ @@ -38,8 +37,6 @@ struct SharedState { notary_signing_key: SigningKey, } -struct Session {} - /// Main entry point for the notary server application. /// /// This function: From a3cdcde9709d8fbf9da8610f6e8d33c2091f665d Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:11:28 -0700 Subject: [PATCH 13/22] feat: test workflows --- .github/workflows/test.yaml | 24 ++++++++++++++++++++++++ core/src/http.rs | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..c357c696f --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,24 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - web-prover-notary + - web-prover-client + - web-prover-core + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: test + + - name: Run tests + run: cargo test -p ${{ matrix.crate }} diff --git a/core/src/http.rs b/core/src/http.rs index a733f1b50..7fa42f6cf 100644 --- a/core/src/http.rs +++ b/core/src/http.rs @@ -703,8 +703,10 @@ pub mod tests { macro_rules! request { // Match with optional parameters ($($key:ident: $value:expr),* $(,)?) => {{ + // Make clippy happy + #[allow(unused_mut) ] let mut request = ManifestRequest { - method: "GET".to_string(), + method: "GET".to_string(), url: "https://example.com".to_string(), version: "HTTP/1.1".to_string(), headers: std::collections::HashMap::from([ From ea791339ce3267a7c845601d44b74727159bb733 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:13:20 -0700 Subject: [PATCH 14/22] add test to `web-prover.yaml` --- .github/workflows/web-prover.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/web-prover.yaml b/.github/workflows/web-prover.yaml index b6d702688..03ce9d738 100644 --- a/.github/workflows/web-prover.yaml +++ b/.github/workflows/web-prover.yaml @@ -15,6 +15,9 @@ jobs: lint: uses: ./.github/workflows/lint.yaml + test: + uses: ./.github/workflows/test.yaml + build_notary: uses: ./.github/workflows/build_notary.yaml From 987b66b552b0493ac3c4564660abb51a819f26a9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:15:15 -0700 Subject: [PATCH 15/22] Update lint.yaml --- .github/workflows/lint.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 225f6c5c5..cd4445f63 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -55,10 +55,15 @@ jobs: rust-cache-key: udeps - name: Install cargo-binstall - run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-github-release.sh | bash + run: | + curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-github-release.sh | bash + source ~/.profile + cargo --version # Verify cargo is available - name: Install cargo-udeps - run: cargo binstall --no-confirm cargo-udeps + run: | + source ~/.profile + cargo binstall --no-confirm cargo-udeps - name: Check unused dependencies run: cargo udeps -p ${{ matrix.crate }} From ff83f0ff6f297b6a8867281707d0fbfccf39c768 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:17:01 -0700 Subject: [PATCH 16/22] remove binstall --- .github/workflows/lint.yaml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index cd4445f63..82946ab90 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -21,7 +21,7 @@ jobs: with: rust-cache-key: clippy - - run: cargo clippy -p ${{ matrix.crate }} -- -D warnings + - run: cargo clippy -p ${{ matrix.crate }} fmt: continue-on-error: true @@ -54,16 +54,8 @@ jobs: with: rust-cache-key: udeps - - name: Install cargo-binstall - run: | - curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-github-release.sh | bash - source ~/.profile - cargo --version # Verify cargo is available - - name: Install cargo-udeps - run: | - source ~/.profile - cargo binstall --no-confirm cargo-udeps + run: cargo install cargo-udeps --locked - name: Check unused dependencies run: cargo udeps -p ${{ matrix.crate }} From 1d43dbaccd1cc51ce8f6ee19c4a6266d2ea7f013 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:25:19 -0700 Subject: [PATCH 17/22] fix: workflows --- .github/workflows/{lint.yaml => check.yaml} | 26 +++++++++++++++++++-- .github/workflows/test.yaml | 24 ------------------- .github/workflows/web-prover.yaml | 7 ++---- 3 files changed, 26 insertions(+), 31 deletions(-) rename .github/workflows/{lint.yaml => check.yaml} (67%) delete mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/check.yaml similarity index 67% rename from .github/workflows/lint.yaml rename to .github/workflows/check.yaml index 82946ab90..f3adf4409 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/check.yaml @@ -1,4 +1,4 @@ -name: Lint +name: Check on: workflow_call: @@ -37,6 +37,25 @@ jobs: - run: cargo fmt --all -- --check + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: + - web-prover-notary + - web-prover-client + - web-prover-core + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: test + + - name: Run tests + run: cargo test -p ${{ matrix.crate }} + udeps: continue-on-error: true runs-on: ubuntu-latest @@ -50,12 +69,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + - uses: ./.github/actions/setup-rust-ubuntu with: rust-cache-key: udeps - name: Install cargo-udeps - run: cargo install cargo-udeps --locked + run: cargo binstall --no-confirm cargo-udeps - name: Check unused dependencies run: cargo udeps -p ${{ matrix.crate }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index c357c696f..000000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: Test - -on: - workflow_call: - -jobs: - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - crate: - - web-prover-notary - - web-prover-client - - web-prover-core - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: test - - - name: Run tests - run: cargo test -p ${{ matrix.crate }} diff --git a/.github/workflows/web-prover.yaml b/.github/workflows/web-prover.yaml index 03ce9d738..800afc877 100644 --- a/.github/workflows/web-prover.yaml +++ b/.github/workflows/web-prover.yaml @@ -12,11 +12,8 @@ concurrency: jobs: - lint: - uses: ./.github/workflows/lint.yaml - - test: - uses: ./.github/workflows/test.yaml + check: + uses: ./.github/workflows/check.yaml build_notary: uses: ./.github/workflows/build_notary.yaml From 54a9ad58759c0ba3a0ab67e7a7b64f12ced23658 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:26:14 -0700 Subject: [PATCH 18/22] Update check.yaml --- .github/workflows/check.yaml | 93 +++++++++++++----------------------- 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index f3adf4409..0eb68ad71 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -4,80 +4,53 @@ on: workflow_call: jobs: - clippy: - continue-on-error: true + check: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - crate: - - web-prover-notary - - web-prover-client - - web-prover-core + check: [clippy, test, udeps] + crate: [web-prover-notary, web-prover-client, web-prover-core] + include: + - check: fmt + crate: all steps: - uses: actions/checkout@v4 + # Setup Rust with cache key based on check type - uses: ./.github/actions/setup-rust-ubuntu with: - rust-cache-key: clippy - - - run: cargo clippy -p ${{ matrix.crate }} - - fmt: - continue-on-error: true - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: fmt - - - run: rustup component add rustfmt - - - run: cargo fmt --all -- --check - - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - crate: - - web-prover-notary - - web-prover-client - - web-prover-core - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: test - - - name: Run tests - run: cargo test -p ${{ matrix.crate }} - - udeps: - continue-on-error: true - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - crate: - - web-prover-notary - - web-prover-client - - web-prover-core - steps: - - uses: actions/checkout@v4 + rust-cache-key: ${{ matrix.check }} + # Install necessary tools based on check type - name: Install cargo-binstall + if: matrix.check == 'udeps' uses: cargo-bins/cargo-binstall@main - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: udeps - - name: Install cargo-udeps + if: matrix.check == 'udeps' run: cargo binstall --no-confirm cargo-udeps + - name: Install rustfmt + if: matrix.check == 'fmt' + run: rustup component add rustfmt + + # Run the appropriate check + - name: Run clippy + if: matrix.check == 'clippy' + continue-on-error: true + run: cargo clippy -p ${{ matrix.crate }} -- -D warnings + + - name: Run tests + if: matrix.check == 'test' + run: cargo test -p ${{ matrix.crate }} + + - name: Check fmt + if: matrix.check == 'fmt' + continue-on-error: true + run: cargo fmt --all -- --check + - name: Check unused dependencies - run: cargo udeps -p ${{ matrix.crate }} + if: matrix.check == 'udeps' + continue-on-error: true + run: cargo udeps -p ${{ matrix.crate }} \ No newline at end of file From 1d870803b6b40e84682532d150a17b854acf15d7 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:30:34 -0700 Subject: [PATCH 19/22] attempt faster check ci --- .github/workflows/check.yaml | 97 +++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 0eb68ad71..d77cc8bd5 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -4,53 +4,94 @@ on: workflow_call: jobs: - check: + clippy: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - check: [clippy, test, udeps] crate: [web-prover-notary, web-prover-client, web-prover-core] - include: - - check: fmt - crate: all steps: - uses: actions/checkout@v4 - # Setup Rust with cache key based on check type - uses: ./.github/actions/setup-rust-ubuntu with: - rust-cache-key: ${{ matrix.check }} + rust-cache-key: clippy-${{ matrix.crate }} - # Install necessary tools based on check type - - name: Install cargo-binstall - if: matrix.check == 'udeps' - uses: cargo-bins/cargo-binstall@main + - name: Run clippy + continue-on-error: true + run: cargo clippy -p ${{ matrix.crate }} + + # Save the target directory to cache for reuse in test job + - name: Save target directory + uses: actions/cache/save@v3 + with: + path: | + target/ + ~/.cargo/registry/ + ~/.cargo/git/ + key: build-cache-${{ matrix.crate }}-${{ github.sha }} - - name: Install cargo-udeps - if: matrix.check == 'udeps' - run: cargo binstall --no-confirm cargo-udeps + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: fmt - - name: Install rustfmt - if: matrix.check == 'fmt' - run: rustup component add rustfmt + - run: rustup component add rustfmt - # Run the appropriate check - - name: Run clippy - if: matrix.check == 'clippy' - continue-on-error: true - run: cargo clippy -p ${{ matrix.crate }} -- -D warnings + - run: cargo fmt --all -- --check + + test: + needs: clippy + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: [web-prover-notary, web-prover-client, web-prover-core] + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: test-${{ matrix.crate }} + + # Restore the target directory from clippy job + - name: Restore cached build + uses: actions/cache/restore@v3 + with: + path: | + target/ + ~/.cargo/registry/ + ~/.cargo/git/ + key: build-cache-${{ matrix.crate }}-${{ github.sha }} + restore-keys: | + build-cache-${{ matrix.crate }}- - name: Run tests - if: matrix.check == 'test' run: cargo test -p ${{ matrix.crate }} - - name: Check fmt - if: matrix.check == 'fmt' - continue-on-error: true - run: cargo fmt --all -- --check + udeps: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + crate: [web-prover-notary, web-prover-client, web-prover-core] + steps: + - uses: actions/checkout@v4 + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + + - uses: ./.github/actions/setup-rust-ubuntu + with: + rust-cache-key: udeps-${{ matrix.crate }} + + - name: Install cargo-udeps + run: cargo binstall --no-confirm cargo-udeps - name: Check unused dependencies - if: matrix.check == 'udeps' continue-on-error: true run: cargo udeps -p ${{ matrix.crate }} \ No newline at end of file From cdd054c187bda5c2621711dbfe775837b784a6b0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 14:33:30 -0700 Subject: [PATCH 20/22] Revert "attempt faster check ci" This reverts commit 1d870803b6b40e84682532d150a17b854acf15d7. --- .github/workflows/check.yaml | 97 +++++++++++------------------------- 1 file changed, 28 insertions(+), 69 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index d77cc8bd5..0eb68ad71 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -4,94 +4,53 @@ on: workflow_call: jobs: - clippy: + check: runs-on: ubuntu-latest strategy: fail-fast: false matrix: + check: [clippy, test, udeps] crate: [web-prover-notary, web-prover-client, web-prover-core] + include: + - check: fmt + crate: all steps: - uses: actions/checkout@v4 + # Setup Rust with cache key based on check type - uses: ./.github/actions/setup-rust-ubuntu with: - rust-cache-key: clippy-${{ matrix.crate }} + rust-cache-key: ${{ matrix.check }} - - name: Run clippy - continue-on-error: true - run: cargo clippy -p ${{ matrix.crate }} - - # Save the target directory to cache for reuse in test job - - name: Save target directory - uses: actions/cache/save@v3 - with: - path: | - target/ - ~/.cargo/registry/ - ~/.cargo/git/ - key: build-cache-${{ matrix.crate }}-${{ github.sha }} - - fmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: fmt - - - run: rustup component add rustfmt + # Install necessary tools based on check type + - name: Install cargo-binstall + if: matrix.check == 'udeps' + uses: cargo-bins/cargo-binstall@main - - run: cargo fmt --all -- --check + - name: Install cargo-udeps + if: matrix.check == 'udeps' + run: cargo binstall --no-confirm cargo-udeps - test: - needs: clippy - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - crate: [web-prover-notary, web-prover-client, web-prover-core] - steps: - - uses: actions/checkout@v4 + - name: Install rustfmt + if: matrix.check == 'fmt' + run: rustup component add rustfmt - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: test-${{ matrix.crate }} - - # Restore the target directory from clippy job - - name: Restore cached build - uses: actions/cache/restore@v3 - with: - path: | - target/ - ~/.cargo/registry/ - ~/.cargo/git/ - key: build-cache-${{ matrix.crate }}-${{ github.sha }} - restore-keys: | - build-cache-${{ matrix.crate }}- + # Run the appropriate check + - name: Run clippy + if: matrix.check == 'clippy' + continue-on-error: true + run: cargo clippy -p ${{ matrix.crate }} -- -D warnings - name: Run tests + if: matrix.check == 'test' run: cargo test -p ${{ matrix.crate }} - udeps: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - crate: [web-prover-notary, web-prover-client, web-prover-core] - steps: - - uses: actions/checkout@v4 - - - name: Install cargo-binstall - uses: cargo-bins/cargo-binstall@main - - - uses: ./.github/actions/setup-rust-ubuntu - with: - rust-cache-key: udeps-${{ matrix.crate }} - - - name: Install cargo-udeps - run: cargo binstall --no-confirm cargo-udeps + - name: Check fmt + if: matrix.check == 'fmt' + continue-on-error: true + run: cargo fmt --all -- --check - name: Check unused dependencies + if: matrix.check == 'udeps' continue-on-error: true run: cargo udeps -p ${{ matrix.crate }} \ No newline at end of file From 8365005b64eaee71a4f4114eaadfcfaed0c959c0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 15:01:56 -0700 Subject: [PATCH 21/22] docs: core modules --- core/src/error.rs | 17 +++++++++++++-- core/src/http.rs | 31 ++++++++++++++++++++++------ core/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++ core/src/manifest.rs | 15 ++++++++++++++ core/src/proof.rs | 16 +++++++++++++++ 5 files changed, 120 insertions(+), 8 deletions(-) diff --git a/core/src/error.rs b/core/src/error.rs index 6c636e37b..11565216f 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -1,15 +1,28 @@ -//! Error type for the `web-prover-core` crate. +//! # Error Types +//! +//! This module defines the error types used throughout the `web-prover-core` crate. +//! +//! The primary error type is [`WebProverCoreError`], which encompasses all possible +//! error conditions that can occur within the crate. use thiserror::Error; -/// Represents the various error conditions that can occur within the `proofs` crate. +/// Represents the various error conditions that can occur within the `web-prover-core` crate. +/// +/// This enum provides specific error variants for different failure scenarios, making +/// error handling more precise and informative. #[derive(Debug, Error)] pub enum WebProverCoreError { /// The error is an invalid manifest + /// + /// This error occurs when a manifest fails validation checks. The string + /// parameter provides details about the specific validation failure. #[error("Invalid manifest: {0}")] InvalidManifest(String), /// Serde operation failed + /// + /// This error occurs when serialization or deserialization operations fail. #[error("Serde error occurred: {0}")] SerdeError(#[from] serde_json::Error), } diff --git a/core/src/http.rs b/core/src/http.rs index 7fa42f6cf..269f2c302 100644 --- a/core/src/http.rs +++ b/core/src/http.rs @@ -1,17 +1,36 @@ //! # HTTP Module //! //! The `http` module provides utilities for handling HTTP-related operations in the proof system. +//! It defines structures and functions for representing, parsing, and validating HTTP requests +//! and responses. //! -//! ## Structs +//! ## Key Components //! -//! - `ResponseBody`: Represents the body of an HTTP response, containing a vector of JSON keys. -//! - `Response`: Represents an HTTP response, including status, version, message, headers, and -//! body. +//! ### Request Handling +//! +//! - [`ManifestRequest`]: Defines the expected HTTP request pattern in a manifest +//! - [`TemplateVar`]: Defines template variables that can be used in requests +//! +//! ### Response Handling +//! +//! - [`ManifestResponse`]: Defines the expected HTTP response pattern in a manifest +//! - [`ManifestResponseBody`]: Represents the body of an expected HTTP response +//! - [`NotaryResponse`]: Represents an actual HTTP response from a notary service +//! - [`NotaryResponseBody`]: Represents the body of an actual HTTP response +//! +//! ### JSON Path Handling +//! +//! - [`JsonKey`]: Represents a key in a JSON path (either a string key or array index) +//! +//! ## Constants +//! +//! - [`MAX_HTTP_HEADERS`]: Maximum number of HTTP headers allowed +//! - [`HTTP_1_1`]: HTTP/1.1 version string //! //! ## Functions //! -//! - `default_version`: Returns the default HTTP version string. -//! - `default_message`: Returns the default HTTP response message string. +//! - [`default_version`]: Returns the default HTTP version string +//! - [`default_message`]: Returns the default HTTP response message string use std::collections::HashMap; diff --git a/core/src/lib.rs b/core/src/lib.rs index e5005aa04..98b2becac 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,3 +1,52 @@ +//! # Web Prover Core +//! +//! `web-prover-core` is a foundational crate for creating and validating proofs of web +//! interactions. It provides the core data structures and validation logic needed to create +//! verifiable proofs that specific HTTP requests were made and specific responses were received. +//! +//! ## Overview +//! +//! This crate implements the core functionality for a system that allows users to: +//! +//! 1. Define a "manifest" that specifies expected HTTP requests and responses +//! 2. Execute those requests in a trusted execution environment (TEE) +//! 3. Generate cryptographic proofs that the specified interactions occurred +//! 4. Verify those proofs +//! +//! ## Key Components +//! +//! - **Manifest**: Defines the expected HTTP request and response patterns +//! - **HTTP**: Utilities for parsing and validating HTTP requests and responses +//! - **Proof**: Data structures for representing cryptographic proofs +//! - **Error**: Error types specific to the web prover system +//! +//! ## Example Usage +//! +//! ```rust,no_run +//! use web_prover_core::{ +//! http::{ManifestRequest, ManifestResponse}, +//! manifest::Manifest, +//! }; +//! +//! // Parse a manifest from JSON +//! let manifest_json = r#"{"request": {...}, "response": {...}}"#; +//! let manifest: Manifest = serde_json::from_str(manifest_json).unwrap(); +//! +//! // Validate the manifest +//! manifest.validate().unwrap(); +//! +//! // Generate a digest for the manifest +//! let digest = manifest.to_keccak_digest().unwrap(); +//! ``` +//! +//! ## Modules +//! +//! - [`manifest`](manifest/index.html): Core manifest data structures and validation +//! - [`http`](http/index.html): HTTP request and response handling +//! - [`proof`](proof/index.html): Proof generation and verification +//! - [`error`](error/index.html): Error types for the crate +//! - [`test_utils`](test_utils/index.html): Utilities for testing + pub mod error; pub mod http; pub mod manifest; diff --git a/core/src/manifest.rs b/core/src/manifest.rs index 5ca2e821e..5466d6118 100644 --- a/core/src/manifest.rs +++ b/core/src/manifest.rs @@ -1,3 +1,18 @@ +//! # Manifest +//! +//! This module defines the core `Manifest` structure that specifies the expected +//! HTTP request and response patterns for web proofs. +//! +//! A manifest serves as a template and validation criteria for HTTP interactions. +//! It defines: +//! +//! - The expected HTTP request (method, URL, headers, body) +//! - The expected HTTP response (status, headers, body) +//! - Validation rules for both request and response +//! +//! Manifests can be serialized to and deserialized from JSON, and can be hashed +//! to create a unique identifier. + use derive_more::From; use serde::{Deserialize, Serialize}; use tiny_keccak::{Hasher, Keccak}; diff --git a/core/src/proof.rs b/core/src/proof.rs index 33d8b5986..0d70292e6 100644 --- a/core/src/proof.rs +++ b/core/src/proof.rs @@ -1,3 +1,19 @@ +//! # Proof +//! +//! This module defines the data structures used to represent cryptographic proofs +//! of web interactions. +//! +//! A proof consists of: +//! +//! - Data about the manifest that was executed +//! - Cryptographic signatures verifying the execution +//! +//! ## Key Components +//! +//! - [`TeeProof`]: The top-level proof structure +//! - [`TeeProofData`]: Contains the data being proven +//! - [`SignedVerificationReply`]: Contains cryptographic signatures and verification data + use std::convert::TryFrom; use serde::{Deserialize, Serialize}; From 05d6c939a26b00ac11fb1f82ee038ffbd80ee51a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 15:10:49 -0700 Subject: [PATCH 22/22] docs: remainder of core crate --- core/src/http.rs | 285 +++++++++++++++++++++++++++++++++++++--------- core/src/lib.rs | 5 +- core/src/proof.rs | 117 +++++++++++++++++-- 3 files changed, 343 insertions(+), 64 deletions(-) diff --git a/core/src/http.rs b/core/src/http.rs index 269f2c302..7899fff21 100644 --- a/core/src/http.rs +++ b/core/src/http.rs @@ -37,26 +37,38 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use tracing::debug; +/// Represents a key in a JSON path, which can be either a string key for objects +/// or a numeric index for arrays. +/// +/// This enum is used to define paths for traversing JSON structures when validating +/// response bodies against expected patterns. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(untagged)] pub enum JsonKey { - /// Object key + /// A string key used to access properties in a JSON object String(String), - /// Array index + /// A numeric index used to access elements in a JSON array Num(usize), } use crate::error::WebProverCoreError; -/// Max HTTP headers +/// Maximum number of HTTP headers allowed in a request or response pub const MAX_HTTP_HEADERS: usize = 25; -/// HTTP/1.1 +/// HTTP/1.1 version string constant pub const HTTP_1_1: &str = "HTTP/1.1"; /// A type of response body used to describe conditions in the client `Manifest`. +/// +/// This structure defines the expected format and content of an HTTP response body +/// in a manifest, particularly focusing on JSON path traversal for validation. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] pub struct ManifestResponseBody { - /// JSON Path containing expected traversal keys and expected value + /// JSON Path containing expected traversal keys and expected value. + /// + /// This vector defines a path through a JSON structure that should be present + /// in the response. Each element in the vector represents a key or index in the + /// path, with the last element typically being the expected value. #[serde(rename = "json")] // TODO: Remove after migrating to a JSON DSL pub json_path: Vec, } @@ -64,6 +76,18 @@ pub struct ManifestResponseBody { impl TryFrom<&[u8]> for ManifestResponseBody { type Error = WebProverCoreError; + /// Attempts to create a `ManifestResponseBody` from a byte slice. + /// + /// If the byte slice is empty, returns a default `ManifestResponseBody` with an empty path. + /// Otherwise, attempts to parse the bytes as a JSON array of `JsonKey` elements. + /// + /// # Arguments + /// + /// * `body_bytes` - The byte slice containing the JSON representation of the path + /// + /// # Returns + /// + /// A `Result` containing either the parsed `ManifestResponseBody` or an error fn try_from(body_bytes: &[u8]) -> Result { if body_bytes.is_empty() { return Ok(Self { json_path: vec![] }); @@ -78,15 +102,33 @@ impl TryFrom<&[u8]> for ManifestResponseBody { /// A type of response body returned by a notary. Must match `ManifestResponseBody` designated /// by the client. +/// +/// This structure represents the actual JSON response body received from a notary service, +/// which will be validated against the expected pattern defined in a `ManifestResponseBody`. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct NotaryResponseBody { /// Raw JSON value returned by a notary. + /// + /// This field contains the parsed JSON response from the notary service. + /// It is `None` if the response body was empty or could not be parsed as JSON. pub json: Option, } impl TryFrom<&[u8]> for NotaryResponseBody { type Error = WebProverCoreError; + /// Attempts to create a `NotaryResponseBody` from a byte slice. + /// + /// If the byte slice is empty, returns a `NotaryResponseBody` with `json` set to `None`. + /// Otherwise, attempts to parse the bytes as a JSON value. + /// + /// # Arguments + /// + /// * `body_bytes` - The byte slice containing the JSON response + /// + /// # Returns + /// + /// A `Result` containing either the parsed `NotaryResponseBody` or an error fn try_from(body_bytes: &[u8]) -> Result { if body_bytes.is_empty() { return Ok(Self { json: None }); @@ -110,6 +152,14 @@ impl NotaryResponseBody { /// /// The function traverses the JSON step-by-step, returning `true` if all keys match, /// or `false` otherwise. + /// + /// # Arguments + /// + /// * `json_path` - A slice of `JsonKey` elements defining the expected path + /// + /// # Returns + /// + /// `true` if the JSON response matches the expected path, `false` otherwise fn matches_path(&self, json_path: &[JsonKey]) -> bool { if json_path.is_empty() { debug!("Invalid json_path: Path is empty"); @@ -164,6 +214,17 @@ impl NotaryResponseBody { false } + /// Handles validation for string keys in the JSON path. + /// + /// # Arguments + /// + /// * `current` - The current JSON value being examined + /// * `expected` - The expected string key or value + /// * `is_last` - Whether this is the last element in the path + /// + /// # Returns + /// + /// `true` if the key exists or the value matches (if last), `false` otherwise fn handle_string_key(&self, current: &serde_json::Value, expected: &str, is_last: bool) -> bool { match current { serde_json::Value::String(actual) => is_last && actual == expected, @@ -175,6 +236,17 @@ impl NotaryResponseBody { } } + /// Handles validation for numeric keys in the JSON path. + /// + /// # Arguments + /// + /// * `current` - The current JSON value being examined + /// * `expected` - The expected array index or numeric value + /// * `is_last` - Whether this is the last element in the path + /// + /// # Returns + /// + /// `true` if the index exists or the value matches (if last), `false` otherwise fn handle_numeric_key( &self, current: &serde_json::Value, @@ -206,20 +278,35 @@ impl NotaryResponseBody { } } -/// A response the made by the notary for a request from the client. Must match client response. +/// A response made by the notary for a request from the client. Must match client response. +/// +/// This structure represents a complete HTTP response from a notary service, including +/// both the response metadata (status, headers, etc.) and the parsed body. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct NotaryResponse { - /// Client-designated response recovered from the notary - pub response: ManifestResponse, - /// Raw response body from the notary + /// Client-designated response recovered from the notary. + /// + /// This field contains the parsed HTTP response metadata, including status code, + /// headers, and other information. + pub response: ManifestResponse, + + /// Raw response body from the notary. + /// + /// This field contains the parsed JSON body of the response, which will be + /// validated against the expected pattern. pub notary_response_body: NotaryResponseBody, } impl NotaryResponse { /// Recovers all `Response` fields from the given payloads and creates a `Response` struct. /// - /// - `header_bytes`: The bytes representing the HTTP response headers and metadata. - /// - `body_bytes`: The bytes representing the HTTP response body. + /// # Arguments + /// + /// * `bytes` - The complete HTTP response as a byte slice, including headers and body + /// + /// # Returns + /// + /// A `Result` containing either the parsed `NotaryResponse` or an error pub fn from_payload(bytes: &[u8]) -> Result { let delimiter = b"\r\n\r\n"; let split_position = bytes @@ -246,7 +333,7 @@ impl NotaryResponse { /// /// # Returns /// - /// The parsed HTTP response header. + /// A tuple containing the parsed headers, status code, HTTP version, and status message. fn parse_header( header_bytes: &[u8], ) -> Result<(HashMap, String, String, String), WebProverCoreError> { @@ -288,8 +375,16 @@ impl NotaryResponse { Ok((headers, status, version, message)) } - /// Tests matching between notary response, `self`, and client-designated response, `other`. + /// Tests matching between notary response, `self`, and client-designated response, `other`. /// Returns true if at least all values in `other` are also present in `self`. + /// + /// # Arguments + /// + /// * `other` - The client-designated response to match against + /// + /// # Returns + /// + /// `true` if the notary response matches the client manifest, `false` otherwise pub fn matches_client_manifest(&self, other: &ManifestResponse) -> bool { if self.response.status != other.status || self.response.version != other.version @@ -331,43 +426,52 @@ impl NotaryResponse { } } -/// Default HTTP version +/// Returns the default HTTP version string (HTTP/1.1) pub fn default_version() -> String { HTTP_1_1.to_string() } -/// Default HTTP message + +/// Returns the default HTTP response message string ("OK") pub fn default_message() -> String { "OK".to_string() } /// Returns an empty `HashMap` as the default value for `vars` fn default_empty_vars() -> HashMap { HashMap::new() } /// HTTP Response items required for circuits +/// +/// This structure defines the expected HTTP response pattern in a manifest, +/// including status code, headers, and body validation rules. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ManifestResponse { - /// HTTP response status - pub status: String, - /// HTTP version + /// HTTP response status code as a string (e.g., "200", "201") + pub status: String, + + /// HTTP version string (e.g., "HTTP/1.1") #[serde(default = "default_version")] pub version: String, - /// HTTP response message + + /// HTTP response status message (e.g., "OK", "Created") #[serde(default = "default_message")] pub message: String, - /// HTTP headers to lock + + /// HTTP headers to validate in the response pub headers: HashMap, - /// HTTP body keys - pub body: ManifestResponseBody, + + /// HTTP body validation rules + pub body: ManifestResponseBody, } impl ManifestResponse { /// Validates the HTTP response /// - /// This function validates the HTTP response. - /// - /// # Arguments - /// - /// * `self`: The HTTP response to validate. + /// This function validates the HTTP response against a set of rules: + /// - Status code must be supported (currently 200 or 201) + /// - HTTP version must be valid (currently only HTTP/1.1) + /// - Message must be of valid length + /// - Headers must include Content-Type with a supported value + /// - JSON path must be valid when Content-Type is application/json /// /// # Returns /// - /// The validated HTTP response. + /// `Ok(())` if the response is valid, or an error describing the validation failure pub fn validate(&self) -> Result<(), WebProverCoreError> { // TODO: What are legal statuses? const VALID_STATUSES: [&str; 2] = ["200", "201"]; @@ -433,45 +537,68 @@ impl ManifestResponse { } /// Template variable type +/// +/// This structure defines constraints for template variables that can be used +/// in HTTP requests, allowing for dynamic content with validation rules. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TemplateVar { - /// Regex for validation (if applicable) - pub regex: Option, - /// Length constraint (if applicable) + /// Regular expression pattern for validating the variable value + /// + /// If provided, the value of the variable must match this regex pattern. + pub regex: Option, + + /// Required length of the variable value + /// + /// If provided, the value of the variable must be exactly this length. pub length: Option, - /// Type constraint (e.g., base64, hex) + + /// Type constraint for the variable + /// + /// If provided, specifies the expected format of the variable (e.g., "base64", "hex"). pub r#type: Option, } /// HTTP Request items required for circuits +/// +/// This structure defines the expected HTTP request pattern in a manifest, +/// including method, URL, headers, and body. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ManifestRequest { - /// HTTP method (GET or POST) - pub method: String, + /// HTTP method (e.g., "GET", "POST") + pub method: String, + /// HTTP request URL - pub url: String, - /// HTTP version + pub url: String, + + /// HTTP version string (e.g., "HTTP/1.1") #[serde(default = "default_version")] pub version: String, - /// Request headers to lock + + /// Request headers to include pub headers: HashMap, - /// Request JSON body - pub body: Option, - /// Request JSON vars to be used in templates + + /// Request JSON body (if any) + pub body: Option, + + /// Template variables that can be used in the request + /// + /// These variables allow for dynamic content in the request while + /// maintaining validation rules. #[serde(default = "default_empty_vars")] - pub vars: HashMap, + pub vars: HashMap, } impl ManifestRequest { - /// This function validates the HTTP request. - /// - /// # Arguments + /// Validates the HTTP request against a set of rules. /// - /// * `self`: The HTTP request to validate. + /// This function checks: + /// - Method must be supported (currently GET or POST) + /// - URL must be valid and use HTTPS + /// - HTTP version must be valid (currently only HTTP/1.1) /// /// # Returns /// - /// The validated HTTP request. + /// `Ok(())` if the request is valid, or an error describing the validation failure pub fn validate(&self) -> Result<(), WebProverCoreError> { // TODO: What HTTP methods are supported? const ALLOWED_METHODS: [&str; 2] = ["GET", "POST"]; @@ -500,6 +627,17 @@ impl ManifestRequest { // TODO (autoparallel): This function is not used anywhere at the moment, but i did not want to // delete it. + + /// Validates template variables in the request. + /// + /// This function performs comprehensive validation of template variables: + /// 1. Ensures all tokens used in the body and headers are declared in `vars` + /// 2. Ensures all variables declared in `vars` are actually used in the request + /// 3. Validates each variable against its constraints (regex, length, type) + /// + /// # Returns + /// + /// `Ok(())` if all variables are valid, or an error describing the validation failure #[allow(unused)] fn validate_vars(&self) -> Result<(), WebProverCoreError> { let mut all_tokens = vec![]; @@ -509,8 +647,7 @@ impl ManifestRequest { for token in &body_tokens { if !self.vars.contains_key(token) { return Err(WebProverCoreError::InvalidManifest(format!( - "Token `<% {} %>` not declared in `vars`", - token + "Token `<% {token} %>` not declared in `vars`", ))); } } @@ -531,6 +668,7 @@ impl ManifestRequest { all_tokens.extend(header_tokens); } + // Ensure all declared variables are actually used somewhere in the request for var_key in self.vars.keys() { if !all_tokens.contains(var_key) { return Err(WebProverCoreError::InvalidManifest(format!( @@ -540,9 +678,9 @@ impl ManifestRequest { } } - // Validate each `vars` entry + // Validate each `vars` entry against its constraints for (key, var_def) in &self.vars { - // Validate regex (if defined) + // Validate regex pattern (if defined) if let Some(regex_pattern) = var_def.regex.as_ref() { // Using `regress` crate for compatibility with ECMAScript regular expressions let _regex = regress::Regex::new(regex_pattern).map_err(|_| { @@ -561,7 +699,7 @@ impl ManifestRequest { // } } - // Validate length (if applicable) + // Validate length constraint (if applicable) if let Some(length) = var_def.length { if let Some(value) = self.body.as_ref().and_then(|b| b.get(key)) { if value.as_str().unwrap_or("").len() != length { @@ -573,12 +711,20 @@ impl ManifestRequest { } } - // TODO: Validate the token "type" constraint + // TODO: Validate the token "type" constraint (e.g., base64, hex) } Ok(()) } /// Parses the HTTP request from the given bytes. + /// + /// # Arguments + /// + /// * `bytes` - The complete HTTP request as a byte slice, including headers and body + /// + /// # Returns + /// + /// A `Result` containing either the parsed `ManifestRequest` or an error pub fn from_payload(bytes: &[u8]) -> Result { // todo: dedup me let delimiter = b"\r\n\r\n"; @@ -603,6 +749,14 @@ impl ManifestRequest { } /// Parses the HTTP request start-line and headers from the given bytes. + /// + /// # Arguments + /// + /// * `header_bytes` - The bytes containing the HTTP request headers + /// + /// # Returns + /// + /// A tuple containing the parsed method, URL, HTTP version, and headers fn parse_header( header_bytes: &[u8], ) -> Result<(String, String, String, HashMap), WebProverCoreError> { @@ -642,10 +796,22 @@ impl ManifestRequest { } /// Checks if the current request is a subset of the given `other` request. + /// /// For the request to be a subset: /// - All headers in `self` must exist in `other` with matching values. /// - All vars in `self` must exist in `other` with matching constraints. /// - All remaining fields like `method`, `url`, and `body` must also match. + /// + /// This function is useful for determining if a request satisfies the requirements + /// specified in another request template. + /// + /// # Arguments + /// + /// * `other` - The request to check against + /// + /// # Returns + /// + /// `true` if this request is a subset of the other request, `false` otherwise pub fn is_subset_of(&self, other: &ManifestRequest) -> bool { // Check if all headers in `self` exist in `other` with the same value for (key, value) in &self.headers { @@ -677,11 +843,24 @@ impl ManifestRequest { } } +/// Extracts template variable tokens from a JSON value. +/// +/// This function recursively searches through a JSON value (string, object, or array) +/// to find all template variable tokens in the format `<% token_name %>`. +/// +/// # Arguments +/// +/// * `value` - The JSON value to search for tokens +/// +/// # Returns +/// +/// A vector of strings containing the extracted token names (without the `<% %>` delimiters) fn extract_tokens(value: &serde_json::Value) -> Vec { let mut tokens = vec![]; match value { serde_json::Value::String(s) => { + // For string values, use regex to find all tokens in the format <% token_name %> let token_regex = regex::Regex::new(r"<%\s*(\w+)\s*%>").unwrap(); for capture in token_regex.captures_iter(s) { if let Some(token) = capture.get(1) { @@ -702,7 +881,7 @@ fn extract_tokens(value: &serde_json::Value) -> Vec { tokens.extend(extract_tokens(v)); } }, - _ => {}, + _ => {}, // Ignore other JSON value types (numbers, booleans, null) } tokens diff --git a/core/src/lib.rs b/core/src/lib.rs index 98b2becac..1d3b8ba8e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -45,11 +45,12 @@ //! - [`http`](http/index.html): HTTP request and response handling //! - [`proof`](proof/index.html): Proof generation and verification //! - [`error`](error/index.html): Error types for the crate -//! - [`test_utils`](test_utils/index.html): Utilities for testing + +#![warn(missing_docs, clippy::missing_docs_in_private_items)] pub mod error; pub mod http; pub mod manifest; pub mod proof; -pub mod test_utils; +#[cfg(test)] mod test_utils; diff --git a/core/src/proof.rs b/core/src/proof.rs index 0d70292e6..bc32ea734 100644 --- a/core/src/proof.rs +++ b/core/src/proof.rs @@ -8,46 +8,145 @@ //! - Data about the manifest that was executed //! - Cryptographic signatures verifying the execution //! +//! ## Overview +//! +//! The proof system is designed to cryptographically verify that a specific HTTP interaction +//! occurred as defined in a manifest. This is accomplished through: +//! +//! 1. Generating a unique hash of the manifest +//! 2. Executing the HTTP request in a trusted execution environment (TEE) +//! 3. Creating a signed proof that includes the manifest hash and response data +//! 4. Providing cryptographic signatures that can be verified by third parties +//! //! ## Key Components //! -//! - [`TeeProof`]: The top-level proof structure -//! - [`TeeProofData`]: Contains the data being proven +//! - [`TeeProof`]: The top-level proof structure that combines proof data with signatures +//! - [`TeeProofData`]: Contains the essential data being proven (manifest hash) //! - [`SignedVerificationReply`]: Contains cryptographic signatures and verification data +//! +//! ## Verification Process +//! +//! To verify a proof: +//! +//! 1. Extract the manifest hash from `TeeProofData` +//! 2. Verify the signature in `SignedVerificationReply` using the provided signer address +//! 3. Confirm that the digest in the signature matches the expected value +//! 4. Optionally verify the Merkle proof if the implementation uses a Merkle tree use std::convert::TryFrom; use serde::{Deserialize, Serialize}; +/// Represents a cryptographically signed verification response from a trusted execution +/// environment. +/// +/// This structure contains all the cryptographic elements needed to verify that a specific +/// manifest was executed in a trusted environment. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SignedVerificationReply { + /// A list of hashes representing the leaves of a Merkle tree. + /// + /// These leaves can be used to construct a Merkle proof, which efficiently verifies + /// that a specific piece of data is included in a larger dataset without requiring + /// the entire dataset. pub merkle_leaves: Vec, - pub digest: String, - pub signature: String, - pub signature_r: String, - pub signature_s: String, - pub signature_v: u8, - pub signer: String, + + /// The cryptographic digest (hash) of the data being verified, typically in hex format. + /// + /// This digest uniquely identifies the content being proven and is what gets signed + /// by the trusted execution environment. + pub digest: String, + + /// The complete ECDSA signature as a single string. + /// + /// This signature is generated by the trusted execution environment and proves that + /// the environment attests to the validity of the digest. + pub signature: String, + + /// The r component of the ECDSA signature. + /// + /// In ECDSA, a signature consists of two values (r,s). This field contains the r value, + /// typically represented as a hexadecimal string. + pub signature_r: String, + + /// The s component of the ECDSA signature. + /// + /// In ECDSA, a signature consists of two values (r,s). This field contains the s value, + /// typically represented as a hexadecimal string. + pub signature_s: String, + + /// The recovery ID for the ECDSA signature. + /// + /// This value (typically 0 or 1) is used to recover the public key from the signature. + /// It's essential for Ethereum-style signature verification. + pub signature_v: u8, + + /// The Ethereum address of the signer. + /// + /// This address represents the public key that signed the digest. Verifiers can use + /// this address to confirm that the signature was created by a trusted entity. + pub signer: String, } +/// The top-level proof structure that combines the proof data with its cryptographic signature. +/// +/// A `TeeProof` represents a complete, verifiable proof that a specific manifest was executed +/// in a trusted execution environment (TEE). #[derive(Debug, Deserialize, Serialize, Clone)] pub struct TeeProof { - pub data: TeeProofData, + /// The core data being proven, including the manifest hash. + /// + /// This field contains the essential information about what is being proven, + /// specifically the cryptographic hash of the manifest that was executed. + pub data: TeeProofData, + + /// Cryptographic signatures and verification data that authenticate the proof. + /// + /// This field contains all the cryptographic elements needed to verify that the + /// proof was generated by a trusted execution environment. pub signature: SignedVerificationReply, } impl TryFrom<&[u8]> for TeeProof { type Error = serde_json::Error; + /// Attempts to deserialize a byte slice into a `TeeProof`. + /// + /// This implementation allows for easy conversion from JSON bytes to a structured proof. + /// + /// # Errors + /// + /// Returns a `serde_json::Error` if deserialization fails. fn try_from(bytes: &[u8]) -> Result { serde_json::from_slice(bytes) } } impl TryFrom for Vec { type Error = serde_json::Error; + /// Attempts to serialize a `TeeProof` into a byte vector. + /// + /// This implementation allows for easy conversion from a structured proof to JSON bytes. + /// + /// # Errors + /// + /// Returns a `serde_json::Error` if serialization fails. fn try_from(proof: TeeProof) -> Result { serde_json::to_vec(&proof) } } +/// Contains the essential data being proven by a `TeeProof`. +/// +/// This structure holds the cryptographic hash of the manifest that was executed, +/// which serves as a unique identifier for the specific HTTP interaction that was proven. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct TeeProofData { + /// A byte vector containing the cryptographic hash of the manifest. + /// + /// This hash uniquely identifies the manifest and can be used to verify that the + /// correct manifest was executed. It's typically a Keccak-256 hash (32 bytes) + /// that serves as a fingerprint of the entire manifest content. + /// + /// Verifiers can independently compute this hash from the original manifest and + /// compare it with the value in the proof to ensure they're verifying the + /// expected interaction. pub manifest_hash: Vec, }