diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..03c3f27
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+## Engineering code of conduct 🤝
+
+### Linter
+
+Use `clippy` to lint your rust code:
+
+```bash
+cargo clippy
+```
+
+### Documentation
+
+We publish dev docs on every push to `GitBook`.
+Check them out at https://distributed-lab.github.io/plonky2-verifier/.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..8eb801e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,41 @@
+name: "🐛 Bug Report"
+description: Create a new ticket for a bug.
+title: "🐛 [BUG] -
"
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: "## Thanks for filing this out ❤️!"
+ - type: textarea
+ id: description
+ attributes:
+ label: "Description"
+ placeholder: Short and explicit description of your incident...
+ validations:
+ required: true
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: "Reproduction steps"
+ placeholder: How can we reproduce the issue?
+ validations:
+ required: false
+ - type: dropdown
+ id: os
+ attributes:
+ label: "OS"
+ description: What is the impacted environment?
+ multiple: true
+ options:
+ - Windows
+ - Linux
+ - Mac
+ validations:
+ required: false
+ - type: textarea
+ id: additional_context
+ attributes:
+ label: "Additional context"
+ placeholder: Provide any additional information.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..0086358
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..6001e51
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,29 @@
+name: "💡 Feature Request"
+description: Create a new ticket for a new feature request
+title: "💡 [REQUEST] - "
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: "## Thanks for filing this out ❤️!"
+ - type: textarea
+ id: summary
+ attributes:
+ label: "Summary"
+ placeholder: Provide a brief explanation of the feature...
+ validations:
+ required: true
+ - type: textarea
+ id: basic_example
+ attributes:
+ label: "Basic Example"
+ placeholder: Provide a basic example of how the feature would work...
+ validations:
+ required: false
+ - type: textarea
+ id: additional_context
+ attributes:
+ label: "Additional context"
+ placeholder: Provide any additional information.
+ validations:
+ required: false
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..4770676
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,55 @@
+name: Deploy Dev Rust Docs to GitHub Pages
+
+on:
+ push:
+ paths:
+ - "**/*.rs" # Any Rust source code files
+ - Cargo.toml # Cargo.toml file
+ - Cargo.lock # Cargo.lock file
+ workflow_dispatch:
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+
+ - name: Build Rust Documentation
+ run: |
+ cargo doc --no-deps --document-private-items
+ echo "" > target/doc/index.html
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./target/doc
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: build
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: dev-docs
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..10c592b
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,135 @@
+name: Rust CI
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ check:
+ name: Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+
+ - name: Run cargo check
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: check
+
+ test:
+ name: Test Suite
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install nightly toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: nightly
+
+ - name: Install Nextest
+ uses: taiki-e/install-action@v2
+ with:
+ tool: nextest
+
+ - name: Run Nextest
+ run: cargo nextest run --all-features
+
+ lints:
+ name: Lints
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+ components: rustfmt, clippy
+
+ - name: Run cargo fmt
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: fmt
+ args: --all -- --check
+
+ - name: Run cargo clippy
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: clippy
+ args: --all-targets --all-features -- -D warnings
diff --git a/README.md b/README.md
index d3c19de..dfcfe77 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# Plonky2 Verifier
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
+[![Rust CI🌌](https://github.com/distributed-lab/plonky2-verifier/actions/workflows/rust.yml/badge.svg)](https://github.com/distributed-lab/plonky2-verifier/actions/workflows/rust.yml)
+[![Docs 🌌](https://github.com/distributed-lab/plonky2-verifier/actions/workflows/docs.yml/badge.svg)](https://github.com/distributed-lab/plonky2-verifier/actions/workflows/docs.yml)
> A verifier for [plonky2](https://github.com/0xPolygonZero/plonky2/) proofs.
diff --git a/src/lib.rs b/src/lib.rs
index 00b9140..41180a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -39,11 +39,6 @@ impl From for VerifyError {
/// Verify the given proof `proof` and public inputs `pubs` using verification key `vk`.
/// Use the given verification key `vk` to verify the proof `proof` against the public inputs `pubs`.
-/// Can fail if:
-/// - the proof, the pubs or the vk are not deserializable respectively as a `plonky2::plonk::proof::Proof`,
-/// a `plonky2::hash::hash_types::RichField + plonky2_field::extension::Extendable`
-/// and `plonky2::plonk::circuit_data::VerifierCircuitData`.
-/// - the proof is not valid.
pub fn verify(
vk: &[u8],
proof: &[u8],
diff --git a/tests/artifacts_generator.rs b/tests/artifacts_generator.rs
index 703541c..20fa1c3 100644
--- a/tests/artifacts_generator.rs
+++ b/tests/artifacts_generator.rs
@@ -7,10 +7,9 @@ use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2::util::serialization::{DefaultGateSerializer, Write};
-/// An example of using Plonky2 to prove a statement of the form
-/// "I know the 100th element of the Fibonacci sequence, starting with constants a and b."
-/// When a == 0 and b == 1, this is proving knowledge of the 100th (standard) Fibonacci number.
-/// This example also serializes the proof, public inputs and vk to files.
+/// Fibonacci circuit, taken from plonky2 examples:
+/// https://github.com/0xPolygonZero/plonky2/blob/v0.2.3/plonky2/examples/fibonacci.rs
+/// Saves proof, public inputs and verification key to artifacts.
pub fn gen_factorial() {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
diff --git a/tests/integration.rs b/tests/integration.rs
index 81d5c22..9f266e6 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -4,61 +4,82 @@ mod artifacts_generator;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2::util::serialization::DefaultGateSerializer;
use plonky2_verifier::{verify, DeserializeError, VerifyError};
+use std::path::Path;
-fn load_data() -> (Vec, Vec, Vec) {
- // Ensure artifacts exist by running `gen_factorial` only if needed
- if !std::path::Path::new("tests/artifacts/proof.bin").exists() ||
- !std::path::Path::new("tests/artifacts/pubs.bin").exists() ||
- !std::path::Path::new("tests/artifacts/vk.bin").exists()
+const PROOF_PATH: &str = "tests/artifacts/proof.bin";
+const PUBS_PATH: &str = "tests/artifacts/pubs.bin";
+const VK_PATH: &str = "tests/artifacts/vk.bin";
+
+/// Proof, public inputs and verification key in serialized format.
+type TestData = (Vec, Vec, Vec);
+
+/// Ensures artifacts are generated and loads them.
+/// Returns Result to handle potential loading errors.
+fn load_data() -> Result {
+ if !Path::new(PROOF_PATH).exists()
+ || !Path::new(PUBS_PATH).exists()
+ || !Path::new(VK_PATH).exists()
{
println!("Generating artifacts...");
artifacts_generator::gen_factorial();
}
- let proof = std::fs::read("tests/artifacts/proof.bin").unwrap();
- let pubs = std::fs::read("tests/artifacts/pubs.bin").unwrap();
- let vk = std::fs::read("tests/artifacts/vk.bin").unwrap();
- (vk, proof, pubs)
+ let proof = std::fs::read(PROOF_PATH).map_err(|e| format!("Failed to load proof: {}", e))?;
+ let pubs = std::fs::read(PUBS_PATH).map_err(|e| format!("Failed to load pubs: {}", e))?;
+ let vk = std::fs::read(VK_PATH).map_err(|e| format!("Failed to load vk: {}", e))?;
+
+ Ok((vk, proof, pubs))
}
+/// Helper to run `verify` with preset types.
fn verify_non_generic(vk: &Vec, proof: &Vec, pubs: &Vec) -> Result<(), VerifyError> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = >::F;
- verify::(vk.as_slice(), proof.as_slice(), pubs.as_slice(), &DefaultGateSerializer)
+ verify::(
+ vk.as_slice(),
+ proof.as_slice(),
+ pubs.as_slice(),
+ &DefaultGateSerializer,
+ )
}
#[test]
fn should_verify_valid_proof() {
- let (vk, proof, pubs) = load_data();
-
+ let (vk, proof, pubs) = load_data().expect("Failed to load data");
assert!(verify_non_generic(&vk, &proof, &pubs).is_ok());
}
#[test]
fn should_not_deserialize_invalid_pubs() {
- let (vk, proof, mut pubs) = load_data();
+ let (vk, proof, mut pubs) = load_data().expect("Failed to load data");
pubs[0] = pubs.first().unwrap().wrapping_add(1);
- assert!(matches!(
- verify_non_generic(&vk, &proof, &pubs),
- Err(VerifyError::InvalidData {
- cause: DeserializeError::InvalidProof
- })
- ));
+ assert!(
+ matches!(
+ verify_non_generic(&vk, &proof, &pubs),
+ Err(VerifyError::InvalidData {
+ cause: DeserializeError::InvalidProof
+ })
+ ),
+ "Expected an InvalidProof error when `pubs` is corrupted"
+ );
}
#[test]
fn should_not_verify_false_proof() {
- let (vk, proof, mut pubs) = load_data();
-
- let len = pubs.len();
- pubs[len - 1] = pubs.last().unwrap().wrapping_add(1);
+ let (vk, mut proof, pubs) = load_data().expect("Failed to load data");
+
+ let len = proof.len();
+ proof[len - 1] = pubs.last().unwrap().wrapping_add(1);
- assert!(matches!(
- verify_non_generic(&vk, &proof, &pubs),
- Err(VerifyError::Failure { .. })
- ));
-}
\ No newline at end of file
+ assert!(
+ matches!(
+ verify_non_generic(&vk, &proof, &pubs),
+ Err(VerifyError::Failure { .. })
+ ),
+ "Expected a Failure error when `proof` is corrupted"
+ );
+}