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

Neptune-Crypto / neptune-core / 15316368194

29 May 2025 04:37AM UTC coverage: 71.791%. First build
15316368194

Pull #603

github

web-flow
Merge 0717e8abc into ac85b2289
Pull Request #603: Correct the proving-capability abstraction for usage with triton-vm Proof (as opposed to TransactionProof)

199 of 257 new or added lines in 16 files covered. (77.43%)

20210 of 28151 relevant lines covered (71.79%)

418176.7 hits per line

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

75.93
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 {
1,114✔
169
        self.transaction_proof_type = Some(transaction_proof_type);
1,114✔
170
        self
1,114✔
171
    }
1,114✔
172

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

256
        let proof_job_options = proof_job_options.ok_or(ProofRequirement::ProofJobOptions)?;
1,210✔
257
        let transaction_proof_type = transaction_proof_type
1,210✔
258
            .unwrap_or_else(|| proof_job_options.job_settings.vm_proving_capability.into());
1,210✔
259

260
        let valid_mock = valid_mock.unwrap_or(true);
1,210✔
261
        let job_queue = job_queue.unwrap_or_else(vm_job_queue);
1,210✔
262

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

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

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

332
        Err(ProofRequirement::TransactionProofInput.into())
×
333
    }
1,210✔
334
}
335

336
// builds TransactionProof::SingleProof from Claim, NonDeterminism
337
//
338
// will generate a mock proof if Network::use_mock_proof() is true.
339
async fn gen_single<'a, F>(
70✔
340
    claim: Claim,
70✔
341
    nondeterminism: F,
70✔
342
    job_queue: Arc<TritonVmJobQueue>,
70✔
343
    proof_job_options: TritonVmProofJobOptions,
70✔
344
    valid_mock: bool,
70✔
345
) -> Result<TransactionProof, CreateProofError>
70✔
346
where
70✔
347
    F: FnOnce() -> NonDeterminism + Send + Sync + 'a,
70✔
348
{
70✔
349
    Ok(TransactionProof::SingleProof(
350
        ProofBuilder::new()
70✔
351
            .program(SingleProof.program())
70✔
352
            .claim(claim)
70✔
353
            .nondeterminism(nondeterminism)
70✔
354
            .job_queue(job_queue)
70✔
355
            .proof_job_options(proof_job_options)
70✔
356
            .valid_mock(valid_mock)
70✔
357
            .build()
70✔
358
            .await?,
70✔
359
    ))
360
}
70✔
361

362
// builds TransactionProof from Cow<PrimitiveWitness>
363
//
364
// will generate a mock proof if Network::use_mock_proof() is true.
365
async fn from_witness(
1,140✔
366
    witness_cow: Cow<'_, PrimitiveWitness>,
1,140✔
367
    job_queue: Arc<TritonVmJobQueue>,
1,140✔
368
    proof_job_options: TritonVmProofJobOptions,
1,140✔
369
    transaction_proof_type: TransactionProofType,
1,140✔
370
    valid_mock: bool,
1,140✔
371
) -> Result<TransactionProof, CreateProofError> {
1,140✔
372
    // generate mock proof, if network uses mock proofs.
373
    if proof_job_options.job_settings.network.use_mock_proof() {
1,140✔
374
        let proof = match transaction_proof_type {
109✔
375
            TransactionProofType::PrimitiveWitness => {
376
                TransactionProof::Witness(witness_cow.into_owned())
105✔
377
            }
378
            TransactionProofType::ProofCollection => {
379
                let pc = ProofCollection::produce_mock(witness_cow.borrow(), valid_mock);
×
380
                TransactionProof::ProofCollection(pc)
×
381
            }
382
            TransactionProofType::SingleProof => {
383
                let sp = SingleProof::produce_mock(valid_mock);
4✔
384
                TransactionProof::SingleProof(sp)
4✔
385
            }
386
        };
387
        return Ok(proof);
109✔
388
    }
1,031✔
389

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;
1,031✔
396
    capability.can_prove(transaction_proof_type)?;
1,031✔
397

398
    // produce proof of requested type
399
    let transaction_proof = match transaction_proof_type {
1,031✔
400
        TransactionProofType::PrimitiveWitness => {
401
            TransactionProof::Witness(witness_cow.into_owned())
930✔
402
        }
403
        TransactionProofType::ProofCollection => TransactionProof::ProofCollection(
404
            ProofCollection::produce(witness_cow.borrow(), job_queue, proof_job_options).await?,
21✔
405
        ),
406
        TransactionProofType::SingleProof => TransactionProof::SingleProof(
407
            SingleProof::produce(witness_cow.borrow(), job_queue, proof_job_options).await?,
80✔
408
        ),
409
    };
410

411
    Ok(transaction_proof)
1,028✔
412
}
1,140✔
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