Replace manual mangement of the Sapling proving context with cxx
Co-authored-by: Jack Grigg <jack@z.cash>
This commit is contained in:
parent
d3e8b3b114
commit
3ef12e98c1
|
@ -1181,9 +1181,8 @@ TEST(ChecktransactionTests, HeartwoodAcceptsSaplingShieldedCoinbase) {
|
|||
libzcash::SaplingSpendingKey::random().default_address(), CAmount(123456), libzcash::Zip212Enabled::BeforeZip212);
|
||||
auto output = OutputDescriptionInfo(ovk, note, {{0xF6}});
|
||||
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
auto odesc = output.Build(ctx).value();
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.fOverwintered = true;
|
||||
|
@ -1286,9 +1285,8 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
|
|||
}
|
||||
|
||||
// Add a Sapling output.
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
auto odesc = output.Build(ctx).value();
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
mtx.vShieldedOutput.push_back(odesc);
|
||||
|
||||
// Coinbase transaction should fail non-contextual checks with valueBalanceSapling
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "validationinterface.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
#include <rust/sapling.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
@ -127,13 +128,13 @@ class AddFundingStreamValueToTx
|
|||
{
|
||||
private:
|
||||
CMutableTransaction &mtx;
|
||||
void* ctx;
|
||||
rust::Box<sapling::Prover>& ctx;
|
||||
const CAmount fundingStreamValue;
|
||||
const libzcash::Zip212Enabled zip212Enabled;
|
||||
public:
|
||||
AddFundingStreamValueToTx(
|
||||
CMutableTransaction &mtx,
|
||||
void* ctx,
|
||||
rust::Box<sapling::Prover>& ctx,
|
||||
const CAmount fundingStreamValue,
|
||||
const libzcash::Zip212Enabled zip212Enabled): mtx(mtx), ctx(ctx), fundingStreamValue(fundingStreamValue), zip212Enabled(zip212Enabled) {}
|
||||
|
||||
|
@ -182,7 +183,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
CAmount SetFoundersRewardAndGetMinerValue(void* ctx) const {
|
||||
CAmount SetFoundersRewardAndGetMinerValue(rust::Box<sapling::Prover>& ctx) const {
|
||||
auto block_subsidy = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||
auto miner_reward = block_subsidy; // founders' reward or funding stream amounts will be subtracted below
|
||||
|
||||
|
@ -197,7 +198,6 @@ public:
|
|||
miner_reward -= fselem.second;
|
||||
bool added = std::visit(AddFundingStreamValueToTx(mtx, ctx, fselem.second, GetZip212Flag()), fselem.first);
|
||||
if (!added) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
throw new std::runtime_error("Failed to add funding stream output.");
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ public:
|
|||
return miner_reward + nFees;
|
||||
}
|
||||
|
||||
void ComputeBindingSig(void* saplingCtx, std::optional<orchard::UnauthorizedBundle> orchardBundle) const {
|
||||
void ComputeBindingSig(rust::Box<sapling::Prover> saplingCtx, std::optional<orchard::UnauthorizedBundle> orchardBundle) const {
|
||||
// Empty output script.
|
||||
uint256 dataToBeSigned;
|
||||
try {
|
||||
|
@ -233,7 +233,6 @@ public:
|
|||
txdata);
|
||||
}
|
||||
} catch (std::logic_error ex) {
|
||||
librustzcash_sapling_proving_ctx_free(saplingCtx);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
|
@ -242,26 +241,23 @@ public:
|
|||
if (authorizedBundle.has_value()) {
|
||||
mtx.orchardBundle = authorizedBundle.value();
|
||||
} else {
|
||||
librustzcash_sapling_proving_ctx_free(saplingCtx);
|
||||
throw new std::runtime_error("Failed to create Orchard proof or signatures");
|
||||
}
|
||||
}
|
||||
|
||||
bool success = librustzcash_sapling_binding_sig(
|
||||
saplingCtx,
|
||||
bool success = saplingCtx->binding_sig(
|
||||
mtx.valueBalanceSapling,
|
||||
dataToBeSigned.begin(),
|
||||
mtx.bindingSig.data());
|
||||
dataToBeSigned.GetRawBytes(),
|
||||
mtx.bindingSig);
|
||||
|
||||
if (!success) {
|
||||
librustzcash_sapling_proving_ctx_free(saplingCtx);
|
||||
throw new std::runtime_error("An error occurred computing the binding signature.");
|
||||
}
|
||||
}
|
||||
|
||||
// Create Orchard output
|
||||
void operator()(const libzcash::OrchardRawAddress &to) const {
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
// `enableSpends` must be set to `false` for coinbase transactions. This
|
||||
// means the Orchard anchor is unconstrained, so we set it to the empty
|
||||
|
@ -289,18 +285,15 @@ public:
|
|||
|
||||
auto bundle = builder.Build();
|
||||
if (!bundle.has_value()) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
throw new std::runtime_error("Failed to create shielded output for miner");
|
||||
}
|
||||
|
||||
ComputeBindingSig(ctx, std::move(bundle));
|
||||
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
ComputeBindingSig(std::move(ctx), std::move(bundle));
|
||||
}
|
||||
|
||||
// Create shielded output
|
||||
void operator()(const libzcash::SaplingPaymentAddress &pa) const {
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
auto miner_reward = SetFoundersRewardAndGetMinerValue(ctx);
|
||||
mtx.valueBalanceSapling -= miner_reward;
|
||||
|
@ -312,20 +305,17 @@ public:
|
|||
|
||||
auto odesc = output.Build(ctx);
|
||||
if (!odesc) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
throw new std::runtime_error("Failed to create shielded output for miner");
|
||||
}
|
||||
mtx.vShieldedOutput.push_back(odesc.value());
|
||||
|
||||
ComputeBindingSig(ctx, std::nullopt);
|
||||
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
ComputeBindingSig(std::move(ctx), std::nullopt);
|
||||
}
|
||||
|
||||
// Create transparent output
|
||||
void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const {
|
||||
// Add the FR output and fetch the miner's output value.
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
// Miner output will be vout[0]; Founders' Reward & funding stream outputs
|
||||
// will follow.
|
||||
|
@ -336,10 +326,8 @@ public:
|
|||
mtx.vout[0] = CTxOut(value, coinbaseScript->reserveScript);
|
||||
|
||||
if (mtx.vShieldedOutput.size() > 0) {
|
||||
ComputeBindingSig(ctx, std::nullopt);
|
||||
ComputeBindingSig(std::move(ctx), std::nullopt);
|
||||
}
|
||||
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -74,55 +74,6 @@ extern "C" {
|
|||
unsigned char *result
|
||||
);
|
||||
|
||||
/// Creates a Sapling proving context. Please free this when you're done.
|
||||
void * librustzcash_sapling_proving_ctx_init();
|
||||
|
||||
/// This function (using the proving context) constructs a Spend proof
|
||||
/// given the necessary witness information. It outputs `cv` (the value
|
||||
/// commitment) and `rk` (so that you don't have to compute it) along
|
||||
/// with the proof.
|
||||
bool librustzcash_sapling_spend_proof(
|
||||
void *ctx,
|
||||
const unsigned char *ak,
|
||||
const unsigned char *nsk,
|
||||
const unsigned char *diversifier,
|
||||
const unsigned char *rcm,
|
||||
const unsigned char *ar,
|
||||
const uint64_t value,
|
||||
const unsigned char *anchor,
|
||||
const unsigned char *witness,
|
||||
unsigned char *cv,
|
||||
unsigned char *rk,
|
||||
unsigned char *zkproof
|
||||
);
|
||||
|
||||
/// This function (using the proving context) constructs an Output
|
||||
/// proof given the necessary witness information. It outputs `cv`
|
||||
/// and the `zkproof`.
|
||||
bool librustzcash_sapling_output_proof(
|
||||
void *ctx,
|
||||
const unsigned char *esk,
|
||||
const unsigned char *payment_address,
|
||||
const unsigned char *rcm,
|
||||
const uint64_t value,
|
||||
unsigned char *cv,
|
||||
unsigned char *zkproof
|
||||
);
|
||||
|
||||
/// This function (using the proving context) constructs a binding
|
||||
/// signature. You must provide the intended valueBalance so that
|
||||
/// we can internally check consistency.
|
||||
bool librustzcash_sapling_binding_sig(
|
||||
const void *ctx,
|
||||
int64_t valueBalance,
|
||||
const unsigned char *sighash,
|
||||
unsigned char *result
|
||||
);
|
||||
|
||||
/// Frees a Sapling proving context returned from
|
||||
/// `librustzcash_sapling_proving_ctx_init`.
|
||||
void librustzcash_sapling_proving_ctx_free(void *);
|
||||
|
||||
/// Compute a Sapling nullifier.
|
||||
///
|
||||
/// The `diversifier` parameter must be 11 bytes in length.
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
// See https://github.com/rust-lang/rfcs/pull/2585 for more background.
|
||||
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
|
||||
use bellman::groth16::{self, prepare_verifying_key, Parameters, PreparedVerifyingKey};
|
||||
use bellman::groth16::{self, Parameters, PreparedVerifyingKey};
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
use bls12_381::Bls12;
|
||||
use group::{cofactor::CofactorGroup, GroupEncoding};
|
||||
|
@ -45,19 +45,14 @@ use std::os::windows::ffi::OsStringExt;
|
|||
|
||||
use zcash_primitives::{
|
||||
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
|
||||
merkle_tree::MerklePath,
|
||||
sapling::{
|
||||
keys::FullViewingKey, note_encryption::sapling_ka_agree, redjubjub, Diversifier, Note,
|
||||
PaymentAddress, ProofGenerationKey, Rseed, ViewingKey,
|
||||
Rseed, ViewingKey,
|
||||
},
|
||||
sapling::{merkle_hash, spend_sig},
|
||||
transaction::components::Amount,
|
||||
zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address},
|
||||
};
|
||||
use zcash_proofs::{
|
||||
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, load_parameters,
|
||||
sapling::SaplingProvingContext, sprout,
|
||||
};
|
||||
use zcash_proofs::{load_parameters, sprout};
|
||||
|
||||
mod blake2b;
|
||||
mod ed25519;
|
||||
|
@ -657,56 +652,6 @@ pub extern "C" fn librustzcash_sprout_verify(
|
|||
)
|
||||
}
|
||||
|
||||
/// This function (using the proving context) constructs an Output proof given
|
||||
/// the necessary witness information. It outputs `cv` and the `zkproof`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_output_proof(
|
||||
ctx: *mut SaplingProvingContext,
|
||||
esk: *const [c_uchar; 32],
|
||||
payment_address: *const [c_uchar; 43],
|
||||
rcm: *const [c_uchar; 32],
|
||||
value: u64,
|
||||
cv: *mut [c_uchar; 32],
|
||||
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||
) -> bool {
|
||||
// Grab `esk`, which the caller should have constructed for the DH key exchange.
|
||||
let esk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*esk })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Grab the payment address from the caller
|
||||
let payment_address = match PaymentAddress::from_bytes(unsafe { &*payment_address }) {
|
||||
Some(pa) => pa,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// The caller provides the commitment randomness for the output note
|
||||
let rcm = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Create proof
|
||||
let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof(
|
||||
esk,
|
||||
payment_address,
|
||||
rcm,
|
||||
value,
|
||||
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
||||
);
|
||||
|
||||
// Write the proof out to the caller
|
||||
proof
|
||||
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||
.expect("should be able to serialize a proof");
|
||||
|
||||
// Write the value commitment to the caller
|
||||
*unsafe { &mut *cv } = value_commitment.to_bytes();
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Computes the signature for each Spend description, given the key `ask`, the
|
||||
/// re-randomization `ar`, the 32-byte sighash `sighash`, and an output `result`
|
||||
/// buffer of 64-bytes for the signature.
|
||||
|
@ -744,148 +689,6 @@ pub extern "C" fn librustzcash_sapling_spend_sig(
|
|||
true
|
||||
}
|
||||
|
||||
/// This function (using the proving context) constructs a binding signature.
|
||||
///
|
||||
/// You must provide the intended valueBalance so that we can internally check
|
||||
/// consistency.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_binding_sig(
|
||||
ctx: *const SaplingProvingContext,
|
||||
value_balance: i64,
|
||||
sighash: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 64],
|
||||
) -> bool {
|
||||
let value_balance = match Amount::from_i64(value_balance) {
|
||||
Ok(vb) => vb,
|
||||
Err(()) => return false,
|
||||
};
|
||||
|
||||
// Sign
|
||||
let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Write out signature
|
||||
sig.write(&mut (unsafe { &mut *result })[..])
|
||||
.expect("result should be 64 bytes");
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// This function (using the proving context) constructs a Spend proof given the
|
||||
/// necessary witness information. It outputs `cv` (the value commitment) and
|
||||
/// `rk` (so that you don't have to compute it) along with the proof.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_spend_proof(
|
||||
ctx: *mut SaplingProvingContext,
|
||||
ak: *const [c_uchar; 32],
|
||||
nsk: *const [c_uchar; 32],
|
||||
diversifier: *const [c_uchar; 11],
|
||||
rcm: *const [c_uchar; 32],
|
||||
ar: *const [c_uchar; 32],
|
||||
value: u64,
|
||||
anchor: *const [c_uchar; 32],
|
||||
merkle_path: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8],
|
||||
cv: *mut [c_uchar; 32],
|
||||
rk_out: *mut [c_uchar; 32],
|
||||
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||
) -> bool {
|
||||
// Grab `ak` from the caller, which should be a point.
|
||||
let ak = match de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*ak })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// `ak` should be prime order.
|
||||
let ak = match de_ct(ak.into_subgroup()) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Grab `nsk` from the caller
|
||||
let nsk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*nsk })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Construct the proof generation key
|
||||
let proof_generation_key = ProofGenerationKey { ak, nsk };
|
||||
|
||||
// Grab the diversifier from the caller
|
||||
let diversifier = Diversifier(unsafe { *diversifier });
|
||||
|
||||
// The caller chooses the note randomness
|
||||
// If this is after ZIP 212, the caller has calculated rcm, and we don't need to call
|
||||
// Note::derive_esk, so we just pretend the note was using this rcm all along.
|
||||
let rseed = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })) {
|
||||
Some(p) => Rseed::BeforeZip212(p),
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// The caller also chooses the re-randomization of ak
|
||||
let ar = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*ar })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// We need to compute the anchor of the Spend.
|
||||
let anchor = match de_ct(bls12_381::Scalar::from_bytes(unsafe { &*anchor })) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Parse the Merkle path from the caller
|
||||
let merkle_path = match MerklePath::from_slice(unsafe { &(&*merkle_path)[..] }) {
|
||||
Ok(w) => w,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Create proof
|
||||
let (proof, value_commitment, rk) = unsafe { &mut *ctx }
|
||||
.spend_proof(
|
||||
proof_generation_key,
|
||||
diversifier,
|
||||
rseed,
|
||||
ar,
|
||||
value,
|
||||
anchor,
|
||||
merkle_path,
|
||||
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
||||
&prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()),
|
||||
)
|
||||
.expect("proving should not fail");
|
||||
|
||||
// Write value commitment to caller
|
||||
*unsafe { &mut *cv } = value_commitment.to_bytes();
|
||||
|
||||
// Write proof out to caller
|
||||
proof
|
||||
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||
.expect("should be able to serialize a proof");
|
||||
|
||||
// Write out `rk` to the caller
|
||||
rk.write(&mut unsafe { &mut *rk_out }[..])
|
||||
.expect("should be able to write to rk_out");
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Creates a Sapling proving context. Please free this when you're done.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
||||
let ctx = Box::new(SaplingProvingContext::new());
|
||||
|
||||
Box::into_raw(ctx)
|
||||
}
|
||||
|
||||
/// Frees a Sapling proving context returned from
|
||||
/// [`librustzcash_sapling_proving_ctx_init`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) {
|
||||
drop(unsafe { Box::from_raw(ctx) });
|
||||
}
|
||||
|
||||
/// Derive the master ExtendedSpendingKey from a seed.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_zip32_sapling_xsk_master(
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use bellman::groth16::{prepare_verifying_key, Proof};
|
||||
use group::GroupEncoding;
|
||||
use group::{cofactor::CofactorGroup, GroupEncoding};
|
||||
|
||||
use rand_core::OsRng;
|
||||
use zcash_note_encryption::EphemeralKeyBytes;
|
||||
use zcash_primitives::{
|
||||
merkle_tree::MerklePath,
|
||||
sapling::{
|
||||
redjubjub::{self, Signature},
|
||||
Nullifier,
|
||||
Diversifier, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
|
||||
},
|
||||
transaction::{
|
||||
components::{sapling, Amount},
|
||||
|
@ -20,10 +21,15 @@ use zcash_primitives::{
|
|||
Authorized, TransactionDigest,
|
||||
},
|
||||
};
|
||||
use zcash_proofs::sapling::{self as sapling_proofs, SaplingVerificationContext};
|
||||
use zcash_proofs::{
|
||||
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
||||
sapling::{self as sapling_proofs, SaplingProvingContext, SaplingVerificationContext},
|
||||
};
|
||||
|
||||
use super::GROTH_PROOF_SIZE;
|
||||
use super::{de_ct, SAPLING_OUTPUT_VK, SAPLING_SPEND_VK};
|
||||
use super::{
|
||||
de_ct, SAPLING_OUTPUT_PARAMS, SAPLING_OUTPUT_VK, SAPLING_SPEND_PARAMS, SAPLING_SPEND_VK,
|
||||
};
|
||||
use crate::bundlecache::{
|
||||
sapling_bundle_validity_cache, sapling_bundle_validity_cache_mut, CacheEntries,
|
||||
};
|
||||
|
@ -58,6 +64,40 @@ mod ffi {
|
|||
binding_sig: [u8; 64],
|
||||
) -> Box<Bundle>;
|
||||
|
||||
type Prover;
|
||||
|
||||
fn init_prover() -> Box<Prover>;
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_spend_proof(
|
||||
self: &mut Prover,
|
||||
ak: &[u8; 32],
|
||||
nsk: &[u8; 32],
|
||||
diversifier: &[u8; 11],
|
||||
rcm: &[u8; 32],
|
||||
ar: &[u8; 32],
|
||||
value: u64,
|
||||
anchor: &[u8; 32],
|
||||
merkle_path: &[u8; 1065], // 1 + 33 * SAPLING_TREE_DEPTH + 8
|
||||
cv: &mut [u8; 32],
|
||||
rk_out: &mut [u8; 32],
|
||||
zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE
|
||||
) -> bool;
|
||||
fn create_output_proof(
|
||||
self: &mut Prover,
|
||||
esk: &[u8; 32],
|
||||
payment_address: &[u8; 43],
|
||||
rcm: &[u8; 32],
|
||||
value: u64,
|
||||
cv: &mut [u8; 32],
|
||||
zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE
|
||||
) -> bool;
|
||||
fn binding_sig(
|
||||
self: &mut Prover,
|
||||
value_balance: i64,
|
||||
sighash: &[u8; 32],
|
||||
result: &mut [u8; 64],
|
||||
) -> bool;
|
||||
|
||||
type Verifier;
|
||||
|
||||
fn init_verifier() -> Box<Verifier>;
|
||||
|
@ -212,6 +252,179 @@ fn finish_bundle_assembly(
|
|||
}))
|
||||
}
|
||||
|
||||
struct Prover(SaplingProvingContext);
|
||||
|
||||
fn init_prover() -> Box<Prover> {
|
||||
Box::new(Prover(SaplingProvingContext::new()))
|
||||
}
|
||||
|
||||
impl Prover {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_spend_proof(
|
||||
&mut self,
|
||||
ak: &[u8; 32],
|
||||
nsk: &[u8; 32],
|
||||
diversifier: &[u8; 11],
|
||||
rcm: &[u8; 32],
|
||||
ar: &[u8; 32],
|
||||
value: u64,
|
||||
anchor: &[u8; 32],
|
||||
merkle_path: &[u8; 1 + 33 * SAPLING_TREE_DEPTH + 8],
|
||||
cv: &mut [u8; 32],
|
||||
rk_out: &mut [u8; 32],
|
||||
zkproof: &mut [u8; GROTH_PROOF_SIZE],
|
||||
) -> bool {
|
||||
// Grab `ak` from the caller, which should be a point.
|
||||
let ak = match de_ct(jubjub::ExtendedPoint::from_bytes(ak)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// `ak` should be prime order.
|
||||
let ak = match de_ct(ak.into_subgroup()) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Grab `nsk` from the caller
|
||||
let nsk = match de_ct(jubjub::Scalar::from_bytes(nsk)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Construct the proof generation key
|
||||
let proof_generation_key = ProofGenerationKey { ak, nsk };
|
||||
|
||||
// Grab the diversifier from the caller
|
||||
let diversifier = Diversifier(*diversifier);
|
||||
|
||||
// The caller chooses the note randomness
|
||||
// If this is after ZIP 212, the caller has calculated rcm, and we don't need to call
|
||||
// Note::derive_esk, so we just pretend the note was using this rcm all along.
|
||||
let rseed = match de_ct(jubjub::Scalar::from_bytes(rcm)) {
|
||||
Some(p) => Rseed::BeforeZip212(p),
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// The caller also chooses the re-randomization of ak
|
||||
let ar = match de_ct(jubjub::Scalar::from_bytes(ar)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// We need to compute the anchor of the Spend.
|
||||
let anchor = match de_ct(bls12_381::Scalar::from_bytes(anchor)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Parse the Merkle path from the caller
|
||||
let merkle_path = match MerklePath::from_slice(merkle_path) {
|
||||
Ok(w) => w,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Create proof
|
||||
let (proof, value_commitment, rk) = self
|
||||
.0
|
||||
.spend_proof(
|
||||
proof_generation_key,
|
||||
diversifier,
|
||||
rseed,
|
||||
ar,
|
||||
value,
|
||||
anchor,
|
||||
merkle_path,
|
||||
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
||||
&prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()),
|
||||
)
|
||||
.expect("proving should not fail");
|
||||
|
||||
// Write value commitment to caller
|
||||
*cv = value_commitment.to_bytes();
|
||||
|
||||
// Write proof out to caller
|
||||
proof
|
||||
.write(&mut zkproof[..])
|
||||
.expect("should be able to serialize a proof");
|
||||
|
||||
// Write out `rk` to the caller
|
||||
rk.write(&mut rk_out[..])
|
||||
.expect("should be able to write to rk_out");
|
||||
|
||||
true
|
||||
}
|
||||
fn create_output_proof(
|
||||
&mut self,
|
||||
esk: &[u8; 32],
|
||||
payment_address: &[u8; 43],
|
||||
rcm: &[u8; 32],
|
||||
value: u64,
|
||||
cv: &mut [u8; 32],
|
||||
zkproof: &mut [u8; GROTH_PROOF_SIZE],
|
||||
) -> bool {
|
||||
// Grab `esk`, which the caller should have constructed for the DH key exchange.
|
||||
let esk = match de_ct(jubjub::Scalar::from_bytes(esk)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Grab the payment address from the caller
|
||||
let payment_address = match PaymentAddress::from_bytes(payment_address) {
|
||||
Some(pa) => pa,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// The caller provides the commitment randomness for the output note
|
||||
let rcm = match de_ct(jubjub::Scalar::from_bytes(rcm)) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Create proof
|
||||
let (proof, value_commitment) = self.0.output_proof(
|
||||
esk,
|
||||
payment_address,
|
||||
rcm,
|
||||
value,
|
||||
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
||||
);
|
||||
|
||||
// Write the proof out to the caller
|
||||
proof
|
||||
.write(&mut zkproof[..])
|
||||
.expect("should be able to serialize a proof");
|
||||
|
||||
// Write the value commitment to the caller
|
||||
*cv = value_commitment.to_bytes();
|
||||
|
||||
true
|
||||
}
|
||||
fn binding_sig(
|
||||
&mut self,
|
||||
value_balance: i64,
|
||||
sighash: &[u8; 32],
|
||||
result: &mut [u8; 64],
|
||||
) -> bool {
|
||||
let value_balance = match Amount::from_i64(value_balance) {
|
||||
Ok(vb) => vb,
|
||||
Err(()) => return false,
|
||||
};
|
||||
|
||||
// Sign
|
||||
let sig = match self.0.binding_sig(value_balance, sighash) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Write out signature
|
||||
sig.write(&mut result[..])
|
||||
.expect("result should be 64 bytes");
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct Verifier(SaplingVerificationContext);
|
||||
|
||||
fn init_verifier() -> Box<Verifier> {
|
||||
|
|
|
@ -125,7 +125,7 @@ SpendDescriptionInfo::SpendDescriptionInfo(
|
|||
librustzcash_sapling_generate_r(alpha.begin());
|
||||
}
|
||||
|
||||
std::optional<OutputDescription> OutputDescriptionInfo::Build(void* ctx) {
|
||||
std::optional<OutputDescription> OutputDescriptionInfo::Build(rust::Box<sapling::Prover>& ctx) {
|
||||
auto cmu = this->note.cmu();
|
||||
if (!cmu) {
|
||||
return std::nullopt;
|
||||
|
@ -143,21 +143,23 @@ std::optional<OutputDescription> OutputDescriptionInfo::Build(void* ctx) {
|
|||
libzcash::SaplingPaymentAddress address(this->note.d, this->note.pk_d);
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << address;
|
||||
std::vector<unsigned char> addressBytes(ss.begin(), ss.end());
|
||||
std::array<unsigned char, 43> addressBytes;
|
||||
std::move(ss.begin(), ss.end(), addressBytes.begin());
|
||||
|
||||
std::array<unsigned char, 32> cvBytes;
|
||||
OutputDescription odesc;
|
||||
uint256 rcm = this->note.rcm();
|
||||
if (!librustzcash_sapling_output_proof(
|
||||
ctx,
|
||||
encryptor.get_esk().begin(),
|
||||
addressBytes.data(),
|
||||
rcm.begin(),
|
||||
if (!ctx->create_output_proof(
|
||||
encryptor.get_esk().GetRawBytes(),
|
||||
addressBytes,
|
||||
rcm.GetRawBytes(),
|
||||
this->note.value(),
|
||||
odesc.cv.begin(),
|
||||
odesc.zkproof.begin())) {
|
||||
cvBytes,
|
||||
odesc.zkproof)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
odesc.cv = uint256::FromRawBytes(cvBytes);
|
||||
odesc.cmu = *cmu;
|
||||
odesc.ephemeralKey = encryptor.get_epk();
|
||||
odesc.encCiphertext = enc.first;
|
||||
|
@ -554,7 +556,7 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
// Sapling spends and outputs
|
||||
//
|
||||
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
// Create Sapling SpendDescriptions
|
||||
for (auto spend : spends) {
|
||||
|
@ -562,33 +564,35 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
auto nf = spend.note.nullifier(
|
||||
spend.expsk.full_viewing_key(), spend.witness.position());
|
||||
if (!cm || !nf) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult("Spend is invalid");
|
||||
}
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << spend.witness.path();
|
||||
std::vector<unsigned char> witness(ss.begin(), ss.end());
|
||||
std::array<unsigned char, 1065> witness;
|
||||
std::move(ss.begin(), ss.end(), witness.begin());
|
||||
|
||||
std::array<unsigned char, 32> cv;
|
||||
std::array<unsigned char, 32> rk;
|
||||
SpendDescription sdesc;
|
||||
uint256 rcm = spend.note.rcm();
|
||||
if (!librustzcash_sapling_spend_proof(
|
||||
ctx,
|
||||
spend.expsk.full_viewing_key().ak.begin(),
|
||||
spend.expsk.nsk.begin(),
|
||||
spend.note.d.data(),
|
||||
rcm.begin(),
|
||||
spend.alpha.begin(),
|
||||
if (!ctx->create_spend_proof(
|
||||
spend.expsk.full_viewing_key().ak.GetRawBytes(),
|
||||
spend.expsk.nsk.GetRawBytes(),
|
||||
spend.note.d,
|
||||
rcm.GetRawBytes(),
|
||||
spend.alpha.GetRawBytes(),
|
||||
spend.note.value(),
|
||||
spend.anchor.begin(),
|
||||
witness.data(),
|
||||
sdesc.cv.begin(),
|
||||
sdesc.rk.begin(),
|
||||
sdesc.zkproof.data())) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
spend.anchor.GetRawBytes(),
|
||||
witness,
|
||||
cv,
|
||||
rk,
|
||||
sdesc.zkproof)) {
|
||||
return TransactionBuilderResult("Spend proof failed");
|
||||
}
|
||||
|
||||
sdesc.cv = uint256::FromRawBytes(cv);
|
||||
sdesc.rk = uint256::FromRawBytes(rk);
|
||||
sdesc.anchor = spend.anchor;
|
||||
sdesc.nullifier = *nf;
|
||||
mtx.vShieldedSpend.push_back(sdesc);
|
||||
|
@ -598,13 +602,11 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
for (auto output : outputs) {
|
||||
// Check this out here as well to provide better logging.
|
||||
if (!output.note.cmu()) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult("Output is invalid");
|
||||
}
|
||||
|
||||
auto odesc = output.Build(ctx);
|
||||
if (!odesc) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult("Failed to create output description");
|
||||
}
|
||||
|
||||
|
@ -623,11 +625,7 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
try {
|
||||
CreateJSDescriptions();
|
||||
} catch (JSDescException e) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult(e.what());
|
||||
} catch (std::runtime_error e) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -649,10 +647,8 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
dataToBeSigned = SignatureHash(scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||
}
|
||||
} catch (std::ios_base::failure ex) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult("Could not construct signature hash: " + std::string(ex.what()));
|
||||
} catch (std::logic_error ex) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return TransactionBuilderResult("Could not construct signature hash: " + std::string(ex.what()));
|
||||
}
|
||||
|
||||
|
@ -674,13 +670,10 @@ TransactionBuilderResult TransactionBuilder::Build()
|
|||
dataToBeSigned.begin(),
|
||||
mtx.vShieldedSpend[i].spendAuthSig.data());
|
||||
}
|
||||
librustzcash_sapling_binding_sig(
|
||||
ctx,
|
||||
ctx->binding_sig(
|
||||
mtx.valueBalanceSapling,
|
||||
dataToBeSigned.begin(),
|
||||
mtx.bindingSig.data());
|
||||
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
dataToBeSigned.GetRawBytes(),
|
||||
mtx.bindingSig);
|
||||
|
||||
// Create Sprout joinSplitSig
|
||||
if (!ed25519_sign(
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <optional>
|
||||
|
||||
#include <rust/builder.h>
|
||||
#include <rust/sapling.h>
|
||||
|
||||
#define NO_MEMO {{0xF6}}
|
||||
|
||||
|
@ -202,7 +203,7 @@ struct OutputDescriptionInfo {
|
|||
libzcash::SaplingNote note,
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo) : ovk(ovk), note(note), memo(memo) {}
|
||||
|
||||
std::optional<OutputDescription> Build(void* ctx);
|
||||
std::optional<OutputDescription> Build(rust::Box<sapling::Prover>& ctx);
|
||||
};
|
||||
|
||||
struct JSDescriptionInfo {
|
||||
|
|
|
@ -760,34 +760,37 @@ double benchmark_create_sapling_spend()
|
|||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << witness.path();
|
||||
std::vector<unsigned char> witnessChars(ss.begin(), ss.end());
|
||||
std::array<unsigned char, 1065> witnessChars;
|
||||
std::move(ss.begin(), ss.end(), witnessChars.begin());
|
||||
|
||||
uint256 alpha;
|
||||
librustzcash_sapling_generate_r(alpha.begin());
|
||||
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
struct timeval tv_start;
|
||||
timer_start(tv_start);
|
||||
|
||||
std::array<unsigned char, 32> cv;
|
||||
std::array<unsigned char, 32> rk;
|
||||
SpendDescription sdesc;
|
||||
uint256 rcm = note.rcm();
|
||||
bool result = librustzcash_sapling_spend_proof(
|
||||
ctx,
|
||||
expsk.full_viewing_key().ak.begin(),
|
||||
expsk.nsk.begin(),
|
||||
note.d.data(),
|
||||
rcm.begin(),
|
||||
alpha.begin(),
|
||||
bool result = ctx->create_spend_proof(
|
||||
expsk.full_viewing_key().ak.GetRawBytes(),
|
||||
expsk.nsk.GetRawBytes(),
|
||||
note.d,
|
||||
rcm.GetRawBytes(),
|
||||
alpha.GetRawBytes(),
|
||||
note.value(),
|
||||
anchor.begin(),
|
||||
witnessChars.data(),
|
||||
sdesc.cv.begin(),
|
||||
sdesc.rk.begin(),
|
||||
sdesc.zkproof.data());
|
||||
anchor.GetRawBytes(),
|
||||
witnessChars,
|
||||
cv,
|
||||
rk,
|
||||
sdesc.zkproof);
|
||||
sdesc.cv = uint256::FromRawBytes(cv);
|
||||
sdesc.rk = uint256::FromRawBytes(rk);
|
||||
|
||||
double t = timer_stop(tv_start);
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
if (!result) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_spend_proof() should return true");
|
||||
}
|
||||
|
@ -813,26 +816,27 @@ double benchmark_create_sapling_output()
|
|||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << address;
|
||||
std::vector<unsigned char> addressBytes(ss.begin(), ss.end());
|
||||
std::array<unsigned char, 43> addressBytes;
|
||||
std::move(ss.begin(), ss.end(), addressBytes.begin());
|
||||
|
||||
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||
auto ctx = sapling::init_prover();
|
||||
|
||||
struct timeval tv_start;
|
||||
timer_start(tv_start);
|
||||
|
||||
std::array<unsigned char, 32> cv;
|
||||
OutputDescription odesc;
|
||||
uint256 rcm = note.rcm();
|
||||
bool result = librustzcash_sapling_output_proof(
|
||||
ctx,
|
||||
encryptor.get_esk().begin(),
|
||||
addressBytes.data(),
|
||||
rcm.begin(),
|
||||
bool result = ctx->create_output_proof(
|
||||
encryptor.get_esk().GetRawBytes(),
|
||||
addressBytes,
|
||||
rcm.GetRawBytes(),
|
||||
note.value(),
|
||||
odesc.cv.begin(),
|
||||
odesc.zkproof.begin());
|
||||
cv,
|
||||
odesc.zkproof);
|
||||
|
||||
odesc.cv = uint256::FromRawBytes(cv);
|
||||
double t = timer_stop(tv_start);
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
if (!result) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_output_proof() should return true");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue