Skip to content

Commit ca608d4

Browse files
committed
refactor: make proving-capability correct for proving
closes #576 TxProvingCapability was being used by ProofBuilder and by ProvingJob but its variants were logically incorrect for these usages because these types only care about NeptuneProof, not about TransactionProof variants, which TxProvingCapability mirrored. This commit fixes that by renaming enum TxProvingCapability to struct VmProvingCapability which is a newtype for log2(padded_height). Some impl<From> are added to TransactionProofType that make it easy to convert to a VmProvingCapability and vice-versa. capability detection is moved from cli_args::Args into VmProvingCapability::auto_detect(). Proof-collection is now log2-padded-height of 15, rather than 11. This is because some tests were failing and required 15 so it is experimentally determined. changes --tx-proving-capability to --vm-proving-capability <int>. removes --max_log2_padded_height_for_proofs as --vm-proving-capability has the same meaning. removes fields proof_type and max_log2_padded_height from ProverJobSettings, as they are no longer relevant. proof_type makes no sense there and max_log2_padded_height is present inside vm_proving_capability. adds method transaction_proof_type() to TransactionProofBuilder, where it actually does make sense. updates tests, adds docs and examples. Squashed: + removed proof_type and max_log2_padded_height from ProverJobSettings. Added transaction_proof_type to TransactionProofBuilder. + re-implemented TxProvingCapability as struct, not enum. + rename TxProvingCapability VmProvingCapability + default tx_proof_type to best proof capable of. other misc + move capability auto-detection from Args into VmProvingCapability + ProofCollection now 15 instead of 11
1 parent 0450a47 commit ca608d4

34 files changed

+731
-640
lines changed

scripts/php/find_slow_tests.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
// This script is useful for finding slow unit tests.
5+
//
6+
// The script sorts the output of `cargo test` by duration of
7+
// each test, and then prints the results in descending order.
8+
//
9+
// It also prints stats at the bottom, eg:
10+
//
11+
// --- unit test stats ---
12+
// Count: 733
13+
// Total: 974.801
14+
// Min: 0.000
15+
// Max: 104.073
16+
// Mean: 1.3298785811733
17+
// Median: 0.118
18+
// ----------------------
19+
//
20+
// As of this writing, it is necessary to use the nightly compiler
21+
// in order to obtain timing for each test.
22+
//
23+
// Here is example usage:
24+
//
25+
// cargo +nightly test -- -Z unstable-options --report-time 2>&1 | tee tests.out
26+
// sort_tests.php tests.out
27+
28+
$file = @$argv[1];
29+
30+
if(!$file || !file_exists($file)) {
31+
die("please provide a file with output of cargo test, eg:\n cargo +nightly test -- -Z unstable-options --report-time 2>&1 | tee tests.out");
32+
}
33+
34+
$lines = file($file);
35+
36+
$a = [];
37+
foreach($lines as $line) {
38+
$fields = explode(' ', $line);
39+
$key = $fields[count($fields)-1];
40+
$key_new = trim(str_replace(['<', '>', 's'], '', $key));
41+
if($key_new != $key && is_numeric($key_new) && substr($line, 0, 5) == "test " && !strstr($line, "finished in")) {
42+
$a[$line] = $key_new;
43+
} else {
44+
// echo "skipping $line";
45+
}
46+
}
47+
48+
arsort($a);
49+
50+
foreach($a as $line => $time) {
51+
echo $line ;
52+
}
53+
54+
echo "\n\n\n";
55+
analyze_float_array(array_values($a));
56+
57+
58+
59+
function analyze_float_array(array $numbers): void {
60+
if (empty($numbers)) {
61+
echo "Error: The input array is empty. Cannot calculate statistics.\n";
62+
return;
63+
}
64+
65+
// --- Min and Max ---
66+
$min = min($numbers);
67+
$max = max($numbers);
68+
69+
// --- Mean (Average) ---
70+
$sum = array_sum($numbers);
71+
$count = count($numbers);
72+
$mean = $sum / $count;
73+
74+
// --- Median ---
75+
// 1. Sort the array numerically.
76+
sort($numbers);
77+
78+
// 2. Determine the middle index(es).
79+
$middleIndex = floor($count / 2);
80+
81+
if ($count % 2 === 1) {
82+
// Odd number of elements: median is the middle element.
83+
$median = $numbers[$middleIndex];
84+
} else {
85+
// Even number of elements: median is the average of the two middle elements.
86+
$median = ($numbers[$middleIndex - 1] + $numbers[$middleIndex]) / 2;
87+
}
88+
89+
// --- Print Results ---
90+
echo "--- unit test stats ---\n";
91+
echo "Count: " . $count . "\n";
92+
echo "Total: " . $sum . "\n";
93+
echo "Min: " . $min . "\n";
94+
echo "Max: " . $max . "\n";
95+
echo "Mean: " . $mean . "\n";
96+
echo "Median: " . $median . "\n";
97+
echo "----------------------\n";
98+
}
99+

src/api/export.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use crate::models::proof_abstractions::timestamp::Timestamp;
1818
pub use crate::models::state::transaction_details::TransactionDetails;
1919
pub use crate::models::state::transaction_kernel_id::TransactionKernelId;
2020
pub use crate::models::state::tx_creation_artifacts::TxCreationArtifacts;
21-
pub use crate::models::state::tx_proving_capability::TxProvingCapability;
21+
pub use crate::models::state::vm_proving_capability::VmProvingCapability;
2222
pub use crate::models::state::wallet::address::generation_address::GenerationSpendingKey;
2323
pub use crate::models::state::wallet::address::symmetric_key::SymmetricKey;
2424
pub use crate::models::state::wallet::address::KeyType;

src/api/tx_initiation/builder/mod.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,11 @@
7474
//! .await?;
7575
//! drop(state_lock); // release lock asap.
7676
//!
77-
//! // use cli options for building proof, but override proof-type
78-
//! let options = TritonVmProofJobOptionsBuilder::new()
79-
//! .template(&gsl.cli().into())
80-
//! .proof_type(TransactionProofType::PrimitiveWitness)
81-
//! .build();
82-
//!
8377
//! // generate simplistic PrimitiveWitness "proof"
8478
//! // This exposes secrets, so tx cannot be broadcast until
8579
//! // proof is upgraded to ProofCollection.
8680
//! let proof = TransactionProofBuilder::new()
8781
//! .transaction_details(&tx_details)
88-
//! .job_queue(vm_job_queue())
8982
//! .proof_job_options(gsl.cli().into())
9083
//! .build()
9184
//! .await?;
@@ -127,17 +120,12 @@
127120
//!
128121
//! # async fn example(tx_details: TransactionDetails, gsl: GlobalStateLock) -> anyhow::Result<TransactionProof> {
129122
//!
130-
//! // specify target proof-type = SingleProof
131-
//! let options = TritonVmProofJobOptionsBuilder::new()
132-
//! .template(&gsl.cli().into())
133-
//! .proof_type(TransactionProofType::SingleProof)
134-
//! .build();
135-
//!
136123
//! // This will take minutes even on a very powerful machine.
137124
//! let proof = TransactionProofBuilder::new()
138125
//! .transaction_details(&tx_details)
126+
//! .transaction_proof_type(TransactionProofType::SingleProof)
139127
//! .job_queue(vm_job_queue())
140-
//! .proof_job_options(options)
128+
//! .proof_job_options(gsl.cli().into())
141129
//! .build()
142130
//! .await?;
143131
//! # Ok(proof)

src/api/tx_initiation/builder/proof_builder.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,11 @@ impl<'a> ProofBuilder<'a> {
196196
// building a real proof.
197197
let nondeterminism = nondeterminism_callback();
198198

199-
let proof_type = proof_job_options.job_settings.proof_type;
200-
let capability = proof_job_options.job_settings.tx_proving_capability;
201-
if !capability.can_prove(proof_type) {
202-
return Err(CreateProofError::TooWeak {
203-
proof_type,
204-
capability,
205-
});
206-
}
207-
// this builder only supports proofs that can be executed in triton-vm.
208-
if !proof_type.executes_in_vm() {
209-
return Err(CreateProofError::NotVmProof(proof_type));
210-
}
199+
proof_job_options
200+
.job_settings
201+
.vm_proving_capability
202+
.check_if_capable_async(program.clone(), claim.clone(), nondeterminism.clone())
203+
.await?;
211204

212205
let job_queue = job_queue.unwrap_or_else(vm_job_queue);
213206

src/api/tx_initiation/builder/transaction_proof_builder.rs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub struct TransactionProofBuilder<'a> {
6262
job_queue: Option<Arc<TritonVmJobQueue>>,
6363
proof_job_options: Option<TritonVmProofJobOptions>,
6464
valid_mock: Option<bool>,
65+
transaction_proof_type: Option<TransactionProofType>,
6566
}
6667

6768
impl<'a> TransactionProofBuilder<'a> {
@@ -161,6 +162,14 @@ impl<'a> TransactionProofBuilder<'a> {
161162
self
162163
}
163164

165+
/// specify type of proof to build (optional)
166+
///
167+
/// default = best proof possible based on [VmProvingCapability](crate::api::export::VmProvingCapability)
168+
pub fn transaction_proof_type(mut self, transaction_proof_type: TransactionProofType) -> Self {
169+
self.transaction_proof_type = Some(transaction_proof_type);
170+
self
171+
}
172+
164173
/// generate the proof.
165174
///
166175
/// ## Required (one-of)
@@ -241,16 +250,19 @@ impl<'a> TransactionProofBuilder<'a> {
241250
job_queue,
242251
proof_job_options,
243252
valid_mock,
253+
transaction_proof_type,
244254
} = self;
245255

246256
let proof_job_options = proof_job_options.ok_or(ProofRequirement::ProofJobOptions)?;
257+
let transaction_proof_type = transaction_proof_type
258+
.unwrap_or_else(|| proof_job_options.job_settings.vm_proving_capability.into());
247259

248260
let valid_mock = valid_mock.unwrap_or(true);
249261
let job_queue = job_queue.unwrap_or_else(vm_job_queue);
250262

251263
// note: evaluation order must match order stated in the method doc-comment.
252264

253-
if proof_job_options.job_settings.proof_type.is_single_proof() {
265+
if transaction_proof_type.is_single_proof() {
254266
// claim, nondeterminism --> single proof
255267
if let Some((c, nd)) = claim_and_nondeterminism {
256268
return gen_single(c, || nd, job_queue, proof_job_options, valid_mock).await;
@@ -284,16 +296,37 @@ impl<'a> TransactionProofBuilder<'a> {
284296

285297
// owned primitive witness --> proof_type
286298
if let Some(w) = primitive_witness {
287-
return from_witness(Cow::Owned(w), job_queue, proof_job_options, valid_mock).await;
299+
return from_witness(
300+
Cow::Owned(w),
301+
job_queue,
302+
proof_job_options,
303+
transaction_proof_type,
304+
valid_mock,
305+
)
306+
.await;
288307
}
289308
// primitive witness reference --> proof_type
290309
else if let Some(w) = primitive_witness_ref {
291-
return from_witness(Cow::Borrowed(w), job_queue, proof_job_options, valid_mock).await;
310+
return from_witness(
311+
Cow::Borrowed(w),
312+
job_queue,
313+
proof_job_options,
314+
transaction_proof_type,
315+
valid_mock,
316+
)
317+
.await;
292318
}
293319
// transaction_details --> proof_type
294320
else if let Some(d) = transaction_details {
295321
let w = d.primitive_witness();
296-
return from_witness(Cow::Owned(w), job_queue, proof_job_options, valid_mock).await;
322+
return from_witness(
323+
Cow::Owned(w),
324+
job_queue,
325+
proof_job_options,
326+
transaction_proof_type,
327+
valid_mock,
328+
)
329+
.await;
297330
}
298331

299332
Err(ProofRequirement::TransactionProofInput.into())
@@ -333,14 +366,12 @@ async fn from_witness(
333366
witness_cow: Cow<'_, PrimitiveWitness>,
334367
job_queue: Arc<TritonVmJobQueue>,
335368
proof_job_options: TritonVmProofJobOptions,
369+
transaction_proof_type: TransactionProofType,
336370
valid_mock: bool,
337371
) -> Result<TransactionProof, CreateProofError> {
338-
let capability = proof_job_options.job_settings.tx_proving_capability;
339-
let proof_type = proof_job_options.job_settings.proof_type;
340-
341372
// generate mock proof, if network uses mock proofs.
342373
if proof_job_options.job_settings.network.use_mock_proof() {
343-
let proof = match proof_type {
374+
let proof = match transaction_proof_type {
344375
TransactionProofType::PrimitiveWitness => {
345376
TransactionProof::Witness(witness_cow.into_owned())
346377
}
@@ -356,16 +387,16 @@ async fn from_witness(
356387
return Ok(proof);
357388
}
358389

359-
// abort early if machine is too weak
360-
if !capability.can_prove(proof_type) {
361-
return Err(CreateProofError::TooWeak {
362-
proof_type,
363-
capability,
364-
});
365-
}
390+
// abort early if machine is too weak.
391+
//
392+
// note: the fallible calls below eventually call ProofBuilder::build()
393+
// which would perform a more expensive verification. Since we know the
394+
// transaction_proof_type here, we can perform this quick check.
395+
let capability = proof_job_options.job_settings.vm_proving_capability;
396+
capability.can_prove(transaction_proof_type)?;
366397

367398
// produce proof of requested type
368-
let transaction_proof = match proof_type {
399+
let transaction_proof = match transaction_proof_type {
369400
TransactionProofType::PrimitiveWitness => {
370401
TransactionProof::Witness(witness_cow.into_owned())
371402
}

0 commit comments

Comments
 (0)