Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions scripts/php/find_slow_tests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env php
<?php

// This script is useful for finding slow unit tests.
//
// The script sorts the output of `cargo test` by duration of
// each test, and then prints the results in descending order.
//
// It also prints stats at the bottom, eg:
//
// --- unit test stats ---
// Count: 733
// Total: 974.801
// Min: 0.000
// Max: 104.073
// Mean: 1.3298785811733
// Median: 0.118
// ----------------------
//
// As of this writing, it is necessary to use the nightly compiler
// in order to obtain timing for each test.
//
// Here is example usage:
//
// cargo +nightly test -- -Z unstable-options --report-time 2>&1 | tee tests.out
// sort_tests.php tests.out

$file = @$argv[1];

if(!$file || !file_exists($file)) {
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");
}

$lines = file($file);

$a = [];
foreach($lines as $line) {
$fields = explode(' ', $line);
$key = $fields[count($fields)-1];
$key_new = trim(str_replace(['<', '>', 's'], '', $key));
if($key_new != $key && is_numeric($key_new) && substr($line, 0, 5) == "test " && !strstr($line, "finished in")) {
$a[$line] = $key_new;
} else {
// echo "skipping $line";
}
}

arsort($a);

foreach($a as $line => $time) {
echo $line ;
}

echo "\n\n\n";
analyze_float_array(array_values($a));



function analyze_float_array(array $numbers): void {
if (empty($numbers)) {
echo "Error: The input array is empty. Cannot calculate statistics.\n";
return;
}

// --- Min and Max ---
$min = min($numbers);
$max = max($numbers);

// --- Mean (Average) ---
$sum = array_sum($numbers);
$count = count($numbers);
$mean = $sum / $count;

// --- Median ---
// 1. Sort the array numerically.
sort($numbers);

// 2. Determine the middle index(es).
$middleIndex = floor($count / 2);

if ($count % 2 === 1) {
// Odd number of elements: median is the middle element.
$median = $numbers[$middleIndex];
} else {
// Even number of elements: median is the average of the two middle elements.
$median = ($numbers[$middleIndex - 1] + $numbers[$middleIndex]) / 2;
}

// --- Print Results ---
echo "--- unit test stats ---\n";
echo "Count: " . $count . "\n";
echo "Total: " . $sum . "\n";
echo "Min: " . $min . "\n";
echo "Max: " . $max . "\n";
echo "Mean: " . $mean . "\n";
echo "Median: " . $median . "\n";
echo "----------------------\n";
}

Comment on lines +1 to +99
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last time I tried to install PHP to run one of the scripts in this repo, I ended up corrupting my operating system and I had to reinstall ubuntu from scratch. (It was certainly not the fault of the script I was trying to run because I had not even gotten to that point.) I am not tempted to try again to see what went wrong the first time.

This is not a vendetta against PHP. I actually think it is a fantastic language and great at what it does. But I do want to raise the question whether it is a good idea to include PHP scripts in this repository. They obviously come with a huge dependency even if the fallout of installing that dependency does not include corrupting the operating system.

Possible alternatives:

  • bash
  • rust
  • python
  • nothing

If the issue is, "who is going to write the bash/rust/python equivalent?" then I wonder how far we get with asking AI to transcribe it.

If the issue is, "who is going to maintain it?" then PHP is the most ill-suited option for random new developers. (Okay, scratch that. Bash is. But PHP is second. The point remains that rust and python are both more accessible and more popular.)

2 changes: 1 addition & 1 deletion src/api/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use crate::models::proof_abstractions::timestamp::Timestamp;
pub use crate::models::state::transaction_details::TransactionDetails;
pub use crate::models::state::transaction_kernel_id::TransactionKernelId;
pub use crate::models::state::tx_creation_artifacts::TxCreationArtifacts;
pub use crate::models::state::tx_proving_capability::TxProvingCapability;
pub use crate::models::state::vm_proving_capability::VmProvingCapability;
pub use crate::models::state::wallet::address::generation_address::GenerationSpendingKey;
pub use crate::models::state::wallet::address::symmetric_key::SymmetricKey;
pub use crate::models::state::wallet::address::KeyType;
Expand Down
16 changes: 2 additions & 14 deletions src/api/tx_initiation/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,11 @@
//! .await?;
//! drop(state_lock); // release lock asap.
//!
//! // use cli options for building proof, but override proof-type
//! let options = TritonVmProofJobOptionsBuilder::new()
//! .template(&gsl.cli().into())
//! .proof_type(TransactionProofType::PrimitiveWitness)
//! .build();
//!
//! // generate simplistic PrimitiveWitness "proof"
//! // This exposes secrets, so tx cannot be broadcast until
//! // proof is upgraded to ProofCollection.
//! let proof = TransactionProofBuilder::new()
//! .transaction_details(&tx_details)
//! .job_queue(vm_job_queue())
//! .proof_job_options(gsl.cli().into())
//! .build()
//! .await?;
Expand Down Expand Up @@ -127,17 +120,12 @@
//!
//! # async fn example(tx_details: TransactionDetails, gsl: GlobalStateLock) -> anyhow::Result<TransactionProof> {
//!
//! // specify target proof-type = SingleProof
//! let options = TritonVmProofJobOptionsBuilder::new()
//! .template(&gsl.cli().into())
//! .proof_type(TransactionProofType::SingleProof)
//! .build();
//!
//! // This will take minutes even on a very powerful machine.
//! let proof = TransactionProofBuilder::new()
//! .transaction_details(&tx_details)
//! .transaction_proof_type(TransactionProofType::SingleProof)
//! .job_queue(vm_job_queue())
//! .proof_job_options(options)
//! .proof_job_options(gsl.cli().into())
//! .build()
//! .await?;
//! # Ok(proof)
Expand Down
17 changes: 5 additions & 12 deletions src/api/tx_initiation/builder/proof_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,11 @@ impl<'a> ProofBuilder<'a> {
// building a real proof.
let nondeterminism = nondeterminism_callback();

let proof_type = proof_job_options.job_settings.proof_type;
let capability = proof_job_options.job_settings.tx_proving_capability;
if !capability.can_prove(proof_type) {
return Err(CreateProofError::TooWeak {
proof_type,
capability,
});
}
// this builder only supports proofs that can be executed in triton-vm.
if !proof_type.executes_in_vm() {
return Err(CreateProofError::NotVmProof(proof_type));
}
proof_job_options
.job_settings
.vm_proving_capability
.check_if_capable_async(program.clone(), claim.clone(), nondeterminism.clone())
.await?;

let job_queue = job_queue.unwrap_or_else(vm_job_queue);

Expand Down
63 changes: 47 additions & 16 deletions src/api/tx_initiation/builder/transaction_proof_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct TransactionProofBuilder<'a> {
job_queue: Option<Arc<TritonVmJobQueue>>,
proof_job_options: Option<TritonVmProofJobOptions>,
valid_mock: Option<bool>,
transaction_proof_type: Option<TransactionProofType>,
}

impl<'a> TransactionProofBuilder<'a> {
Expand Down Expand Up @@ -161,6 +162,14 @@ impl<'a> TransactionProofBuilder<'a> {
self
}

/// specify type of proof to build (optional)
///
/// default = best proof possible based on [VmProvingCapability](crate::api::export::VmProvingCapability)
pub fn transaction_proof_type(mut self, transaction_proof_type: TransactionProofType) -> Self {
self.transaction_proof_type = Some(transaction_proof_type);
self
}

/// generate the proof.
///
/// ## Required (one-of)
Expand Down Expand Up @@ -241,16 +250,19 @@ impl<'a> TransactionProofBuilder<'a> {
job_queue,
proof_job_options,
valid_mock,
transaction_proof_type,
} = self;

let proof_job_options = proof_job_options.ok_or(ProofRequirement::ProofJobOptions)?;
let transaction_proof_type = transaction_proof_type
.unwrap_or_else(|| proof_job_options.job_settings.vm_proving_capability.into());
Comment on lines +259 to +260
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .into() hides a lot of logic. If I understand correctly, it is mapping a VmProvingCapability to a TransactionProofType by selecting the TransactionProofType whose corresponding VmProvingCapability is the largest subject to being smaller than the given vm_proving_capability. A mapping as complex and non-trivial as that warrants an explicit, descriptive name. For instance: most_complex_transaction_proof_possible.

In contrast, .into() should be reserved for cases where target type and concrete mapping are so obvious that the extra explicit indication of a dedicated function can be omitted.


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

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

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

// owned primitive witness --> proof_type
if let Some(w) = primitive_witness {
return from_witness(Cow::Owned(w), job_queue, proof_job_options, valid_mock).await;
return from_witness(
Cow::Owned(w),
job_queue,
proof_job_options,
transaction_proof_type,
valid_mock,
)
.await;
}
// primitive witness reference --> proof_type
else if let Some(w) = primitive_witness_ref {
return from_witness(Cow::Borrowed(w), job_queue, proof_job_options, valid_mock).await;
return from_witness(
Cow::Borrowed(w),
job_queue,
proof_job_options,
transaction_proof_type,
valid_mock,
)
.await;
}
// transaction_details --> proof_type
else if let Some(d) = transaction_details {
let w = d.primitive_witness();
return from_witness(Cow::Owned(w), job_queue, proof_job_options, valid_mock).await;
return from_witness(
Cow::Owned(w),
job_queue,
proof_job_options,
transaction_proof_type,
valid_mock,
)
.await;
}

Err(ProofRequirement::TransactionProofInput.into())
Expand Down Expand Up @@ -333,14 +366,12 @@ async fn from_witness(
witness_cow: Cow<'_, PrimitiveWitness>,
job_queue: Arc<TritonVmJobQueue>,
proof_job_options: TritonVmProofJobOptions,
transaction_proof_type: TransactionProofType,
valid_mock: bool,
) -> Result<TransactionProof, CreateProofError> {
let capability = proof_job_options.job_settings.tx_proving_capability;
let proof_type = proof_job_options.job_settings.proof_type;

// generate mock proof, if network uses mock proofs.
if proof_job_options.job_settings.network.use_mock_proof() {
let proof = match proof_type {
let proof = match transaction_proof_type {
TransactionProofType::PrimitiveWitness => {
TransactionProof::Witness(witness_cow.into_owned())
}
Expand All @@ -356,16 +387,16 @@ async fn from_witness(
return Ok(proof);
}

// abort early if machine is too weak
if !capability.can_prove(proof_type) {
return Err(CreateProofError::TooWeak {
proof_type,
capability,
});
}
// abort early if machine is too weak.
//
// note: the fallible calls below eventually call ProofBuilder::build()
// which would perform a more expensive verification. Since we know the
// transaction_proof_type here, we can perform this quick check.
let capability = proof_job_options.job_settings.vm_proving_capability;
capability.can_prove(transaction_proof_type)?;

// produce proof of requested type
let transaction_proof = match proof_type {
let transaction_proof = match transaction_proof_type {
TransactionProofType::PrimitiveWitness => {
TransactionProof::Witness(witness_cow.into_owned())
}
Expand Down
Loading
Loading