• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Neptune-Crypto / neptune-core / 15525349195

09 Jun 2025 02:20AM UTC coverage: 71.724%. First build
15525349195

Pull #603

github

web-flow
Merge 20705ddf9 into 2b903ca0f
Pull Request #603: Correct the proving-capability abstraction for usage with triton-vm Proof (as opposed to TransactionProof)

219 of 294 new or added lines in 17 files covered. (74.49%)

20204 of 28169 relevant lines covered (71.72%)

410551.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.79
/src/api/tx_initiation/builder/transaction_proof_builder.rs
1
//! This module implements a builder for transaction proofs.
2
//!
3
//! There are different levels of [TransactionProof] that
4
//! can be generated.  The desired proof can be specified with [TransactionProofType].
5
//!
6
//! With exception of `TransactionProofType::PrimitiveWitness`, proof generation
7
//! is a very CPU and RAM intensive process.  Each type of proof has different
8
//! hardware requirements.  Also the complexity is affected by the type and size
9
//! of transaction.
10
//!
11
//! Before a transaction can be confirmed in a block it must have a SingleProof
12
//! which is the hardest proof to generate.
13
//!
14
//! see [Transaction Initiation Sequence](super::super#transaction-initiation-sequence)
15
//!
16
//! If you have a powerful enough machine, you can generate a ProofCollection or
17
//! SingleProof yourself before passing the transaction to neptune-core.  This
18
//! takes load off the entire network and may lower the transaction fee
19
//! requirements.
20
//!
21
//! see [Client Provides Proof Initiation Sequence](super::super#client-provides-proof-initiation-sequence)
22
//!
23
//! see [builder](super) for examples of using the builders together.
24

25
use std::borrow::Borrow;
26
use std::borrow::Cow;
27
use std::sync::Arc;
28

29
use super::proof_builder::ProofBuilder;
30
use crate::api::tx_initiation::error::CreateProofError;
31
use crate::api::tx_initiation::error::ProofRequirement;
32
use crate::models::blockchain::transaction::primitive_witness::PrimitiveWitness;
33
use crate::models::blockchain::transaction::transaction_proof::TransactionProofType;
34
use crate::models::blockchain::transaction::validity::proof_collection::ProofCollection;
35
use crate::models::blockchain::transaction::validity::single_proof::SingleProof;
36
use crate::models::blockchain::transaction::validity::single_proof::SingleProofWitness;
37
use crate::models::blockchain::transaction::TransactionProof;
38
use crate::models::proof_abstractions::tasm::program::ConsensusProgram;
39
use crate::models::proof_abstractions::tasm::program::TritonVmProofJobOptions;
40
use crate::models::proof_abstractions::SecretWitness;
41
use crate::models::state::transaction_details::TransactionDetails;
42
use crate::triton_vm::proof::Claim;
43
use crate::triton_vm::vm::NonDeterminism;
44
use crate::triton_vm_job_queue::vm_job_queue;
45
use crate::triton_vm_job_queue::TritonVmJobQueue;
46
use crate::util_types::log_vm_state;
47
use crate::util_types::log_vm_state::LogProofInputsType;
48

49
/// a builder for [TransactionProof]
50
///
51
/// see [module docs](self) for details.
52
#[derive(Debug, Default)]
53
pub struct TransactionProofBuilder<'a> {
54
    // these 3 types apply to any TransactionProofType
55
    transaction_details: Option<&'a TransactionDetails>,
56
    primitive_witness: Option<PrimitiveWitness>,
57
    primitive_witness_ref: Option<&'a PrimitiveWitness>,
58

59
    // these 3 types apply only to SingleProof
60
    proof_collection: Option<ProofCollection>,
61
    single_proof_witness: Option<&'a SingleProofWitness>,
62
    claim_and_nondeterminism: Option<(Claim, NonDeterminism)>,
63

64
    job_queue: Option<Arc<TritonVmJobQueue>>,
65
    proof_job_options: Option<TritonVmProofJobOptions>,
66
    valid_mock: Option<bool>,
67
    transaction_proof_type: Option<TransactionProofType>,
68
}
69

70
impl<'a> TransactionProofBuilder<'a> {
71
    /// instantiate
72
    pub fn new() -> Self {
1,213✔
73
        Default::default()
1,213✔
74
    }
1,213✔
75

76
    /// add transaction details (optional)
77
    pub fn transaction_details(mut self, transaction_details: &'a TransactionDetails) -> Self {
1,003✔
78
        self.transaction_details = Some(transaction_details);
1,003✔
79
        self
1,003✔
80
    }
1,003✔
81

82
    /// add primitive witness (optional)
83
    ///
84
    /// Note that a `PrimitiveWitness` contains a `TransactionKernel` which is a
85
    /// field of `Transaction`.  Thus when generating a transaction it can avoid
86
    /// duplicate work to generate a witness, clone the kernel, provide the
87
    /// witness here, and then provide the kernel to the `TransactionBuilder`.
88
    ///
89
    /// It is also possible and may be more convenient to work only with
90
    /// `TransactionDetails`.
91
    pub fn primitive_witness(mut self, witness: PrimitiveWitness) -> Self {
1,003✔
92
        self.primitive_witness = Some(witness);
1,003✔
93
        self
1,003✔
94
    }
1,003✔
95

96
    /// add transaction details reference (optional)
97
    ///
98
    /// Note that if the target proof-type is `PrimitiveWitness` then the
99
    /// reference will be cloned when building and it may be better to use the
100
    /// `primitive_witness()` method.
101
    pub fn primitive_witness_ref(mut self, witness: &'a PrimitiveWitness) -> Self {
130✔
102
        self.primitive_witness_ref = Some(witness);
130✔
103
        self
130✔
104
    }
130✔
105

106
    /// add proof collection (optional)
107
    ///
108
    /// only used for building single-proof
109
    pub fn proof_collection(mut self, proof_collection: ProofCollection) -> Self {
×
110
        self.proof_collection = Some(proof_collection);
×
111
        self
×
112
    }
×
113

114
    /// add single proof witness (optional)
115
    ///
116
    /// only used for building single-proof
117
    pub fn single_proof_witness(mut self, single_proof_witness: &'a SingleProofWitness) -> Self {
70✔
118
        self.single_proof_witness = Some(single_proof_witness);
70✔
119
        self
70✔
120
    }
70✔
121

122
    /// add claim and non-determinism (optional)
123
    ///
124
    /// only used for building single-proof
125
    pub fn claim_and_nondeterminism(
×
126
        mut self,
×
127
        claim_and_nondeterminism: (Claim, NonDeterminism),
×
128
    ) -> Self {
×
129
        self.claim_and_nondeterminism = Some(claim_and_nondeterminism);
×
130
        self
×
131
    }
×
132

133
    /// add job queue (optional)
134
    ///
135
    /// if not provided then the process-wide [vm_job_queue()] will be used.
136
    pub fn job_queue(mut self, job_queue: Arc<TritonVmJobQueue>) -> Self {
1,196✔
137
        self.job_queue = Some(job_queue);
1,196✔
138
        self
1,196✔
139
    }
1,196✔
140

141
    /// add job options. (required)
142
    ///
143
    /// note: can be obtained via `TritonVmProofJobOptions::from(Args)`
144
    ///
145
    /// There is also `TritonVmProofJobOptionsBuilder`
146
    pub fn proof_job_options(mut self, proof_job_options: TritonVmProofJobOptions) -> Self {
1,213✔
147
        self.proof_job_options = Some(proof_job_options);
1,213✔
148
        self
1,213✔
149
    }
1,213✔
150

151
    /// create valid or invalid mock proof. (optional)
152
    ///
153
    /// valid mock proofs pass proof verification; invalid mock proofs do not.
154
    ///
155
    /// see [NeptuneProof](crate::models::blockchain::transaction::validity::neptune_proof::NeptuneProof)
156
    ///
157
    /// default = true
158
    ///
159
    /// only applies if the network uses mock proofs, eg regtest.
160
    ///
161
    /// does not apply to TransactionProof::PrimitiveWitness
162
    pub fn valid_mock(mut self, valid_mock: bool) -> Self {
×
163
        self.valid_mock = Some(valid_mock);
×
164
        self
×
165
    }
×
166

167
    /// specify type of proof to build (optional)
168
    ///
169
    /// default = best proof possible based on [VmProvingCapability](crate::api::export::VmProvingCapability)
170
    pub fn transaction_proof_type(mut self, transaction_proof_type: TransactionProofType) -> Self {
1,117✔
171
        self.transaction_proof_type = Some(transaction_proof_type);
1,117✔
172
        self
1,117✔
173
    }
1,117✔
174

175
    /// generate the proof.
176
    ///
177
    /// ## Required (one-of)
178
    ///
179
    /// The following are individually optional, but at least one must be
180
    /// provided, else an error will result. (ProofRequirement::TransactionProofInput)
181
    ///
182
    /// * transaction_details()
183
    /// * primitive_witness()
184
    /// * primitive_witness_ref()
185
    /// * proof_collection()
186
    /// * single_proof_witness()
187
    /// * claim_and_nondeterminism()
188
    ///
189
    /// ## Evaluation
190
    ///
191
    /// if the network uses mock proofs (eg Network::RegTest), this will return
192
    /// immediately with a mock `TransactionProof`.
193
    ///
194
    /// if the target proof-type is Witness, this will return immediately.
195
    ///
196
    /// otherwise it will initiate an async job that could take many minutes.
197
    ///
198
    /// The provided inputs are evaluated in the following order, which
199
    /// generally is in order of most to least efficient. Evaluation ends at the
200
    /// first match in this list:
201
    ///
202
    /// if target proof_type is SingleProof:
203
    /// * claim_and_nondeterminism()
204
    /// * single_proof_witness()
205
    /// * proof_collection()
206
    ///
207
    /// for any proof-type:
208
    /// * primitive_witness()
209
    /// * primitive_witness_ref()
210
    /// * transaction_details()
211
    ///
212
    /// note that these jobs occur in a global (per process) job queue that only
213
    /// permits one VM job to process at a time.  This prevents parallel jobs
214
    /// from bringing the machine to its knees when each is using all available
215
    /// CPU cores and RAM.
216
    ///
217
    /// Given the serialized nature of the job-queue, it is possible or even likely
218
    /// that other jobs may precede this one.
219
    ///
220
    /// One can query the job_queue to determine how many jobs are in the queue.
221
    ///
222
    /// ## RegTest mode
223
    ///
224
    /// mock proofs are used on the regtest network (only) because they can be
225
    /// generated instantly.
226
    ///
227
    /// ## External Process
228
    ///
229
    /// Proofs are generated in the Triton VM. The proof generation occurs in a
230
    /// separate executable, `triton-vm-prover`, which is spawned by the
231
    /// job-queue for each proving job.  Only one `triton-vm-prover` process
232
    /// should be executing at a time for a given neptune-core instance.
233
    ///
234
    /// If the external process is killed for any reason, the proof-generation job will fail
235
    /// and this method will return an error.
236
    ///
237
    /// ## Cancellation
238
    ///
239
    /// See [TritonVmProofJobOptions::cancel_job_rx].
240
    ///
241
    /// note that cancelling the future returned by build() will NOT cancel the
242
    /// job in the job-queue, as that runs in a separately spawned tokio task
243
    /// managed by the job-queue.
244
    pub async fn build(self) -> Result<TransactionProof, CreateProofError> {
1,213✔
245
        let TransactionProofBuilder {
246
            transaction_details,
1,213✔
247
            primitive_witness,
1,213✔
248
            primitive_witness_ref,
1,213✔
249
            claim_and_nondeterminism,
1,213✔
250
            single_proof_witness,
1,213✔
251
            proof_collection,
1,213✔
252
            job_queue,
1,213✔
253
            proof_job_options,
1,213✔
254
            valid_mock,
1,213✔
255
            transaction_proof_type,
1,213✔
256
        } = self;
1,213✔
257

258
        let proof_job_options = proof_job_options.ok_or(ProofRequirement::ProofJobOptions)?;
1,213✔
259
        let transaction_proof_type = transaction_proof_type
1,213✔
260
            .unwrap_or_else(|| proof_job_options.job_settings.vm_proving_capability.into());
1,213✔
261

262
        let valid_mock = valid_mock.unwrap_or(true);
1,213✔
263
        let job_queue = job_queue.unwrap_or_else(vm_job_queue);
1,213✔
264

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

267
        if transaction_proof_type.is_single_proof() {
1,213✔
268
            // claim, nondeterminism --> single proof
269
            if let Some((c, nd)) = claim_and_nondeterminism {
155✔
270
                return gen_single(c, || nd, job_queue, proof_job_options, valid_mock).await;
×
271
            }
272
            // single-proof-witness --> single proof
273
            else if let Some(w) = single_proof_witness {
155✔
274
                let c = w.claim();
70✔
275
                return gen_single(
70✔
276
                    c,
70✔
277
                    || w.nondeterminism(),
70✔
278
                    job_queue,
70✔
279
                    proof_job_options,
70✔
280
                    valid_mock,
70✔
281
                )
282
                .await;
70✔
283
            }
284
            // proof-collection --> single proof
285
            else if let Some(pc) = proof_collection {
85✔
286
                let w = SingleProofWitness::from_collection(pc);
×
287
                let c = w.claim();
×
288
                return gen_single(
×
289
                    c,
×
290
                    || w.nondeterminism(),
×
291
                    job_queue,
×
292
                    proof_job_options,
×
293
                    valid_mock,
×
294
                )
295
                .await;
×
296
            }
85✔
297
        }
1,058✔
298

299
        // owned primitive witness --> proof_type
300
        if let Some(w) = primitive_witness {
1,143✔
301
            return from_witness(
1,003✔
302
                Cow::Owned(w),
1,003✔
303
                job_queue,
1,003✔
304
                proof_job_options,
1,003✔
305
                transaction_proof_type,
1,003✔
306
                valid_mock,
1,003✔
307
            )
1,003✔
308
            .await;
1,003✔
309
        }
310
        // primitive witness reference --> proof_type
311
        else if let Some(w) = primitive_witness_ref {
140✔
312
            return from_witness(
140✔
313
                Cow::Borrowed(w),
140✔
314
                job_queue,
140✔
315
                proof_job_options,
140✔
316
                transaction_proof_type,
140✔
317
                valid_mock,
140✔
318
            )
140✔
319
            .await;
140✔
320
        }
321
        // transaction_details --> proof_type
322
        else if let Some(d) = transaction_details {
×
323
            let w = d.primitive_witness();
×
NEW
324
            return from_witness(
×
NEW
325
                Cow::Owned(w),
×
NEW
326
                job_queue,
×
NEW
327
                proof_job_options,
×
NEW
328
                transaction_proof_type,
×
NEW
329
                valid_mock,
×
NEW
330
            )
×
NEW
331
            .await;
×
332
        }
×
333

334
        Err(ProofRequirement::TransactionProofInput.into())
×
335
    }
1,213✔
336
}
337

338
// builds TransactionProof::SingleProof from Claim, NonDeterminism
339
//
340
// will generate a mock proof if Network::use_mock_proof() is true.
341
async fn gen_single<'a, F>(
70✔
342
    claim: Claim,
70✔
343
    nondeterminism: F,
70✔
344
    job_queue: Arc<TritonVmJobQueue>,
70✔
345
    proof_job_options: TritonVmProofJobOptions,
70✔
346
    valid_mock: bool,
70✔
347
) -> Result<TransactionProof, CreateProofError>
70✔
348
where
70✔
349
    F: Clone + FnOnce() -> NonDeterminism + Send + Sync + 'a,
70✔
350
{
70✔
351
    // log proof inputs if matching env var is set. (does not expose witness secrets)
352
    // maybe_write() logs warning if error occurs; we ignore any error.
353
    let _ = log_vm_state::maybe_write(
70✔
354
        LogProofInputsType::DoesNotContainWalletSecrets,
70✔
355
        SingleProof.program(),
70✔
356
        &claim,
70✔
357
        nondeterminism.clone(),
70✔
358
    );
70✔
359

360
    Ok(TransactionProof::SingleProof(
361
        ProofBuilder::new()
70✔
362
            .program(SingleProof.program())
70✔
363
            .claim(claim)
70✔
364
            .nondeterminism(nondeterminism)
70✔
365
            .job_queue(job_queue)
70✔
366
            .proof_job_options(proof_job_options)
70✔
367
            .valid_mock(valid_mock)
70✔
368
            .build()
70✔
369
            .await?,
70✔
370
    ))
371
}
70✔
372

373
// builds TransactionProof from Cow<PrimitiveWitness>
374
//
375
// will generate a mock proof if Network::use_mock_proof() is true.
376
async fn from_witness(
1,143✔
377
    witness_cow: Cow<'_, PrimitiveWitness>,
1,143✔
378
    job_queue: Arc<TritonVmJobQueue>,
1,143✔
379
    proof_job_options: TritonVmProofJobOptions,
1,143✔
380
    transaction_proof_type: TransactionProofType,
1,143✔
381
    valid_mock: bool,
1,143✔
382
) -> Result<TransactionProof, CreateProofError> {
1,143✔
383
    // generate mock proof, if network uses mock proofs.
384
    if proof_job_options.job_settings.network.use_mock_proof() {
1,143✔
385
        let proof = match transaction_proof_type {
110✔
386
            TransactionProofType::PrimitiveWitness => {
387
                TransactionProof::Witness(witness_cow.into_owned())
105✔
388
            }
389
            TransactionProofType::ProofCollection => {
390
                let pc = ProofCollection::produce_mock(witness_cow.borrow(), valid_mock);
×
391
                TransactionProof::ProofCollection(pc)
×
392
            }
393
            TransactionProofType::SingleProof => {
394
                let sp = SingleProof::produce_mock(valid_mock);
5✔
395
                TransactionProof::SingleProof(sp)
5✔
396
            }
397
        };
398
        return Ok(proof);
110✔
399
    }
1,033✔
400

401
    // abort early if machine is too weak.
402
    //
403
    // note: the fallible calls below eventually call ProofBuilder::build()
404
    // which would perform a more expensive verification.  Since we know the
405
    // transaction_proof_type here, we can perform this quick check.
406
    let capability = proof_job_options.job_settings.vm_proving_capability;
1,033✔
407
    capability.can_prove(transaction_proof_type)?;
1,033✔
408

409
    // produce proof of requested type
410
    let transaction_proof = match transaction_proof_type {
1,033✔
411
        TransactionProofType::PrimitiveWitness => {
412
            TransactionProof::Witness(witness_cow.into_owned())
932✔
413
        }
414
        TransactionProofType::ProofCollection => TransactionProof::ProofCollection(
415
            ProofCollection::produce(witness_cow.borrow(), job_queue, proof_job_options).await?,
21✔
416
        ),
417
        TransactionProofType::SingleProof => TransactionProof::SingleProof(
418
            SingleProof::produce(witness_cow.borrow(), job_queue, proof_job_options).await?,
80✔
419
        ),
420
    };
421

422
    Ok(transaction_proof)
1,030✔
423
}
1,143✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc