Relax RngCore type constraints.

The intent of this change is to permit tests to use deterministic
random sources, rather than cryptographically strong ones.
This commit is contained in:
Kris Nuttycombe 2020-10-21 12:37:21 -06:00
parent c289cf9d4b
commit cf0c3ea90f
9 changed files with 131 additions and 18 deletions

View File

@ -4,7 +4,7 @@
//! light clients.
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#![deny(broken_intra_doc_links)]
pub mod address;
mod decrypt;

View File

@ -5,7 +5,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#![deny(broken_intra_doc_links)]
pub mod block;
pub mod consensus;

View File

@ -237,7 +237,7 @@ pub fn prf_ock(
/// let encCiphertext = enc.encrypt_note_plaintext();
/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu);
/// ```
pub struct SaplingNoteEncryption<R: RngCore + CryptoRng> {
pub struct SaplingNoteEncryption<R: RngCore> {
epk: jubjub::SubgroupPoint,
esk: jubjub::Fr,
note: Note,
@ -254,13 +254,25 @@ impl<R: RngCore + CryptoRng> SaplingNoteEncryption<R> {
/// Setting `ovk` to `None` represents the `ovk = ⊥` case, where the note cannot be
/// recovered by the sender.
pub fn new(
ovk: Option<OutgoingViewingKey>,
note: Note,
to: PaymentAddress,
memo: Memo,
rng: R,
) -> Self {
Self::new_internal(ovk, note, to, memo, rng)
}
}
impl<R: RngCore> SaplingNoteEncryption<R> {
pub(crate) fn new_internal(
ovk: Option<OutgoingViewingKey>,
note: Note,
to: PaymentAddress,
memo: Memo,
mut rng: R,
) -> Self {
let esk = note.generate_or_derive_esk(&mut rng);
let esk = note.generate_or_derive_esk_internal(&mut rng);
let epk = note.g_d * esk;
SaplingNoteEncryption {

View File

@ -292,6 +292,10 @@ impl Note {
}
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
self.generate_or_derive_esk_internal(rng)
}
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> jubjub::Fr {
match self.derive_esk() {
None => {
// create random 64 byte buffer

View File

@ -59,7 +59,7 @@ pub trait TxProver {
) -> Result<Signature, ()>;
}
#[cfg(test)]
#[cfg(any(test, feature = "test-dependencies"))]
pub mod mock {
use ff::Field;
use rand_core::OsRng;
@ -78,9 +78,8 @@ pub mod mock {
use super::TxProver;
pub(crate) struct MockTxProver;
pub struct MockTxProver;
#[cfg(test)]
impl TxProver for MockTxProver {
type SaplingProvingContext = ();

View File

@ -114,6 +114,15 @@ pub fn spend_sig<R: RngCore + CryptoRng>(
ar: jubjub::Fr,
sighash: &[u8; 32],
rng: &mut R,
) -> Signature {
spend_sig_internal(ask, ar, sighash, rng)
}
pub(crate) fn spend_sig_internal<R: RngCore>(
ask: PrivateKey,
ar: jubjub::Fr,
sighash: &[u8; 32],
rng: &mut R,
) -> Signature {
// We compute `rsk`...
let rsk = ask.randomize(ar);

View File

@ -19,14 +19,14 @@ use crate::{
primitives::{Diversifier, Note, PaymentAddress},
prover::TxProver,
redjubjub::PrivateKey,
sapling::{spend_sig, Node},
sapling::{spend_sig_internal, Node},
transaction::{
components::{
amount::Amount, amount::DEFAULT_FEE, OutputDescription, SpendDescription, TxOut,
},
signature_hash_data, SignableInput, Transaction, TransactionData, SIGHASH_ALL,
},
util::generate_random_rseed,
util::generate_random_rseed_internal,
zip32::ExtendedSpendingKey,
};
@ -42,6 +42,10 @@ use crate::{
#[cfg(any(feature = "transparent-inputs", feature = "zfuture"))]
use crate::transaction::components::OutPoint;
#[cfg(any(test, feature = "test-dependencies"))]
use crate::prover::mock::MockTxProver;
const DEFAULT_TX_EXPIRY_DELTA: u32 = 20;
/// If there are any shielded inputs, always have at least two shielded outputs, padding
@ -107,6 +111,18 @@ impl SaplingOutput {
to: PaymentAddress,
value: Amount,
memo: Option<Memo>,
) -> Result<Self, Error> {
Self::new_internal(params, height, rng, ovk, to, value, memo)
}
fn new_internal<R: RngCore, P: consensus::Parameters>(
params: &P,
height: BlockHeight,
rng: &mut R,
ovk: Option<OutgoingViewingKey>,
to: PaymentAddress,
value: Amount,
memo: Option<Memo>,
) -> Result<Self, Error> {
let g_d = match to.g_d() {
Some(g_d) => g_d,
@ -116,7 +132,7 @@ impl SaplingOutput {
return Err(Error::InvalidAmount);
}
let rseed = generate_random_rseed(params, height, rng);
let rseed = generate_random_rseed_internal(params, height, rng);
let note = Note {
g_d,
@ -139,7 +155,16 @@ impl SaplingOutput {
ctx: &mut P::SaplingProvingContext,
rng: &mut R,
) -> OutputDescription {
let mut encryptor = SaplingNoteEncryption::new(
self.build_internal(prover, ctx, rng)
}
fn build_internal<P: TxProver, R: RngCore>(
self,
prover: &P,
ctx: &mut P::SaplingProvingContext,
rng: &mut R,
) -> OutputDescription {
let mut encryptor = SaplingNoteEncryption::new_internal(
self.ovk,
self.note.clone(),
self.to.clone(),
@ -341,7 +366,7 @@ impl TransactionMetadata {
}
/// Generates a [`Transaction`] from its inputs and outputs.
pub struct Builder<'a, P: consensus::Parameters, R: RngCore + CryptoRng> {
pub struct Builder<'a, P: consensus::Parameters, R: RngCore> {
params: P,
rng: R,
height: BlockHeight,
@ -423,8 +448,13 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
pub fn new_with_rng_zfuture(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> {
Self::new_with_mtx(params, height, rng, TransactionData::zfuture())
}
}
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
/// Common utility function for builder construction.
///
/// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION
/// OF BUILDERS WITH NON-CryptoRng RNGs
fn new_with_mtx(
params: P,
height: BlockHeight,
@ -495,7 +525,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
value: Amount,
memo: Option<Memo>,
) -> Result<(), Error> {
let output = SaplingOutput::new(
let output = SaplingOutput::new_internal(
&self.params,
self.height,
&mut self.rng,
@ -700,7 +730,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
// Record the post-randomized output location
tx_metadata.output_indices[pos] = i;
output.build(prover, &mut ctx, &mut self.rng)
output.build_internal(prover, &mut ctx, &mut self.rng)
} else {
// This is a dummy output
let (dummy_to, dummy_note) = {
@ -727,7 +757,8 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
}
};
let rseed = generate_random_rseed(&self.params, self.height, &mut self.rng);
let rseed =
generate_random_rseed_internal(&self.params, self.height, &mut self.rng);
(
payment_address,
@ -740,7 +771,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
)
};
let esk = dummy_note.generate_or_derive_esk(&mut self.rng);
let esk = dummy_note.generate_or_derive_esk_internal(&mut self.rng);
let epk = dummy_note.g_d * esk;
let (zkproof, cv) = prover.output_proof(
@ -785,7 +816,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
// Create Sapling spendAuth and binding signatures
for (i, (_, spend)) in spends.into_iter().enumerate() {
self.mtx.shielded_spends[i].spend_auth_sig = Some(spend_sig(
self.mtx.shielded_spends[i].spend_auth_sig = Some(spend_sig_internal(
PrivateKey(spend.extsk.expsk.ask),
spend.alpha,
&sighash,
@ -878,6 +909,56 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a
}
}
#[cfg(any(test, feature = "test-dependencies"))]
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height
/// and randomness source, using default values for general transaction fields.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
///
/// WARNING: DO NOT USE IN PRODUCTION
pub fn test_only_new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> {
Self::new_with_mtx(params, height, rng, TransactionData::new())
}
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
/// and randomness source, using default values for general transaction fields
/// and the `ZFUTURE_TX_VERSION` and `ZFUTURE_VERSION_GROUP_ID` version identifiers.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
///
/// The transaction will be constructed and serialized according to the
/// NetworkUpgrade::ZFuture rules. This is intended only for use in
/// integration testing of new features.
///
/// WARNING: DO NOT USE IN PRODUCTION
#[cfg(feature = "zfuture")]
pub fn test_only_new_with_rng_zfuture(
params: P,
height: BlockHeight,
rng: R,
) -> Builder<'a, P, R> {
Self::new_with_mtx(params, height, rng, TransactionData::zfuture())
}
pub fn mock_build(
self,
consensus_branch_id: consensus::BranchId,
) -> Result<(Transaction, TransactionMetadata), Error> {
self.build(consensus_branch_id, &MockTxProver)
}
}
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField};

View File

@ -22,6 +22,14 @@ pub fn generate_random_rseed<P: consensus::Parameters, R: RngCore + CryptoRng>(
params: &P,
height: BlockHeight,
rng: &mut R,
) -> Rseed {
generate_random_rseed_internal(params, height, rng)
}
pub(crate) fn generate_random_rseed_internal<P: consensus::Parameters, R: RngCore>(
params: &P,
height: BlockHeight,
rng: &mut R,
) -> Rseed {
if params.is_nu_active(NetworkUpgrade::Canopy, height) {
let mut buffer = [0u8; 32];

View File

@ -5,7 +5,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#![deny(broken_intra_doc_links)]
use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey};
use bls12_381::Bls12;