Replace manual mangement of the Sapling proving context with cxx

Co-authored-by: Jack Grigg <jack@z.cash>
This commit is contained in:
Kris Nuttycombe 2022-08-25 19:25:10 -06:00
parent d3e8b3b114
commit 3ef12e98c1
8 changed files with 299 additions and 348 deletions

View File

@ -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

View File

@ -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);
}
};

View File

@ -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.

View File

@ -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(

View File

@ -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> {

View File

@ -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(

View File

@ -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 {

View File

@ -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");
}