Skip to content

Commit

Permalink
feat!: Add field version to Claim
Browse files Browse the repository at this point in the history
The explicit version simplifies upgrading proofs for users of Triton VM.
  • Loading branch information
jan-ferdinand committed Oct 2, 2024
1 parent 5a6963d commit 0d5b25d
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ package = "triton-isa"
anyhow = "1.0"
arbitrary = { version = "1", features = ["derive"] }
assert2 = "0.3"
bincode = "1.3.3"
colored = "2.1"
clap = { version = "4", features = ["derive", "cargo", "wrap_help", "unicode", "string"] }
criterion = { version = "0.5", features = ["html_reports"] }
Expand Down
1 change: 1 addition & 0 deletions triton-vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ unicode-width.workspace = true

[dev-dependencies]
assert2.workspace = true
bincode.workspace = true
cargo-husky.workspace = true
constraint-circuit.workspace = true
fs-err.workspace = true
Expand Down
8 changes: 3 additions & 5 deletions triton-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,9 @@ pub fn prove_program(
// While it is more convenient to construct a `Claim::about_program(&program)`, this API is
// purposefully not used here to highlight that only a program's hash digest, not the full
// program, is part of the claim.
let claim = Claim {
program_digest: program.hash(),
input: public_input.individual_tokens,
output: public_output,
};
let claim = Claim::new(program.hash())
.with_input(public_input)
.with_output(public_output);

// The default parameters give a (conjectured) security level of 160 bits.
let stark = Stark::default();
Expand Down
82 changes: 80 additions & 2 deletions triton-vm/src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ use twenty_first::prelude::*;
use crate::error::ProofStreamError;
use crate::proof_stream::ProofStream;

/// A version tag for the combination of Triton VM's
/// [instruction set architecture (ISA)][isa] as well as the
/// [STARK proof system][crate::stark::Stark].
/// This version changes whenever either of the two changes.
///
/// # Rationale
///
/// A change in the ISA might give a [`Program`] a new meaning, and an existing
/// proof might erroneously attest to the “new” program's graceful halt. By
/// bumping this version when changing the ISA, the old proof is surely invalid
/// under the new version. If the program's meaning has not changed, or the new
/// meaning is accepted, a new proof can be generated.
///
/// A change in the STARK proof system generally means that the verifier has to
/// perform different operations to verify a proof. This means that existing
/// proofs about some program _should_ be accepted as valid, but (generally) are
/// not. This version helps to make the discrepancy explicit.
///
/// Note that proofs remain valid for their matching versions indefinitely.
///
/// This version is separate from the crate's semantic version to allow software
/// upgrades with no semantic changes to both, the ISA and the proof system.
pub const CURRENT_VERSION: u32 = 0;

/// Contains the necessary cryptographic information to verify a computation.
/// Should be used together with a [`Claim`].
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, GetSize, BFieldCodec, Arbitrary)]
Expand Down Expand Up @@ -43,9 +67,16 @@ impl Proof {
Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, GetSize, BFieldCodec, Arbitrary,
)]
pub struct Claim {
/// The hash digest of the program that was executed. The hash function in use is Tip5.
/// The hash digest of the program that was executed. The hash function in use is [`Tip5`].
pub program_digest: Digest,

/// The version of the Triton VM instruction set architecture the
/// [`program_digest`][digest] is about, as well as of the STARK proof system
/// in use. See also: [`CURRENT_VERSION`].
///
/// [digest]: Self::program_digest
pub version: u32,

/// The public input to the computation.
pub input: Vec<BFieldElement>,

Expand All @@ -54,9 +85,14 @@ pub struct Claim {
}

impl Claim {
/// Create a new Claim.
///
/// Assumes the version to be [`CURRENT_VERSION`]. The version can be changed
/// with method [`about_version`][Self::about_version].
pub fn new(program_digest: Digest) -> Self {
Self {
program_digest,
version: CURRENT_VERSION,
input: vec![],
output: vec![],
}
Expand All @@ -78,18 +114,25 @@ impl Claim {
self.output = output;
self
}

#[must_use]
pub fn about_version(mut self, version: u32) -> Self {
self.version = version;
self
}
}

#[cfg(test)]
mod tests {
use assert2::assert;
use fs_err as fs;
use proptest::collection::vec;
use proptest::prelude::*;
use proptest_arbitrary_interop::arb;
use test_strategy::proptest;

use crate::prelude::*;
use crate::proof_item::ProofItem;
use crate::vm::PublicInput;

use super::*;

Expand Down Expand Up @@ -148,4 +191,39 @@ mod tests {
) {
let _proof = Proof::decode(&proof_data);
}

#[test]
fn current_proof_version_is_still_current() {
// todo: Once the prover can be de-randomized, change this test. In particular:
// Seed prover, generate proof, hash the proof, compare to hardcoded digest.
// Remove the proof stored on disk, and remove dependency `bincode`.

fn generate_proof_file(program: Program, claim: Claim) {
let input = claim.input.clone().into();
let non_determinism = NonDeterminism::default();
let (aet, _) = VM::trace_execution(&program, input, non_determinism).unwrap();
let proof = Stark::default().prove(&claim, &aet).unwrap();
let proof = bincode::serialize(&proof).unwrap();
fs::create_dir_all("./test_data/").unwrap();
fs::write("./test_data/current_version_is_current.proof.new", proof).unwrap();
eprintln!("New proof generated. Rename & commit to accept.");
}

let program = triton_program! {
pick 11 pick 12 pick 13 pick 14 pick 15
read_io 5 assert_vector halt
};
let claim = Claim::about_program(&program).with_input(program.hash());

let Ok(proof) = fs::read("./test_data/current_version_is_current.proof") else {
generate_proof_file(program, claim);
panic!("Proof file does not exist.");
};
let proof = bincode::deserialize(&proof).unwrap();

if Stark::default().verify(&claim, &proof).is_err() {
generate_proof_file(program, claim);
panic!("Verification of existing proof failed. Need to bump proof version?");
};
}
}
Binary file not shown.

0 comments on commit 0d5b25d

Please sign in to comment.