Merge pull request #6000 from ebfull/enablezip216forall

Enable ZIP 216 for blocks prior to NU5 activation
This commit is contained in:
str4d 2022-07-04 15:49:26 +01:00 committed by GitHub
commit 466ea88539
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 258 additions and 277 deletions

View File

@ -48,15 +48,18 @@ endif
CXXBRIDGE_RS = \
rust/src/blake2b.rs \
rust/src/equihash.rs \
rust/src/orchard_bundle.rs
rust/src/orchard_bundle.rs \
rust/src/sapling.rs
CXXBRIDGE_H = \
rust/gen/include/rust/blake2b.h \
rust/gen/include/rust/equihash.h \
rust/gen/include/rust/orchard_bundle.h
rust/gen/include/rust/orchard_bundle.h \
rust/gen/include/rust/sapling.h
CXXBRIDGE_CPP = \
rust/gen/src/blake2b.cpp \
rust/gen/src/equihash.cpp \
rust/gen/src/orchard_bundle.cpp
rust/gen/src/orchard_bundle.cpp \
rust/gen/src/sapling.cpp
# We add a rust/cxx.h include to indicate that we provide this (via the rustcxx depends
# package), so that cxxbridge doesn't include it within the generated headers and code.

View File

@ -16,6 +16,7 @@
#include "librustzcash.h"
#include <rust/ed25519.h>
#include <rust/sapling.h>
static void ECDSA(benchmark::State& state)
{
@ -106,21 +107,19 @@ static void SaplingSpend(benchmark::State& state)
ss >> spend;
uint256 dataToBeSigned = uint256S("0x2dbf83fe7b88a7cbd80fac0c719483906bb9a0c4fc69071e4780d5f2c76e592c");
auto ctx = librustzcash_sapling_verification_ctx_init(true);
auto ctx = sapling::init_verifier();
while (state.KeepRunning()) {
librustzcash_sapling_check_spend(
ctx,
spend.cv.begin(),
spend.anchor.begin(),
spend.nullifier.begin(),
spend.rk.begin(),
spend.zkproof.begin(),
spend.spendAuthSig.begin(),
dataToBeSigned.begin());
ctx->check_spend(
spend.cv.GetRawBytes(),
spend.anchor.GetRawBytes(),
spend.nullifier.GetRawBytes(),
spend.rk.GetRawBytes(),
spend.zkproof,
spend.spendAuthSig,
dataToBeSigned.GetRawBytes()
);
}
librustzcash_sapling_verification_ctx_free(ctx);
}
static void SaplingOutput(benchmark::State& state)
@ -132,18 +131,16 @@ static void SaplingOutput(benchmark::State& state)
PROTOCOL_VERSION);
ss >> output;
auto ctx = librustzcash_sapling_verification_ctx_init(true);
auto ctx = sapling::init_verifier();
while (state.KeepRunning()) {
librustzcash_sapling_check_output(
ctx,
output.cv.begin(),
output.cmu.begin(),
output.ephemeralKey.begin(),
output.zkproof.begin());
ctx->check_output(
output.cv.GetRawBytes(),
output.cmu.GetRawBytes(),
output.ephemeralKey.GetRawBytes(),
output.zkproof
);
}
librustzcash_sapling_verification_ctx_free(ctx);
}
BENCHMARK(ECDSA);

View File

@ -135,7 +135,8 @@ public:
uint256S("00000000002038016f976744c369dce7419fca30e7171dfac703af5e5f7ad1d4");
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170100;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = 1687104;
consensus.vUpgrades[Consensus::UPGRADE_NU5].hashActivationBlock =
uint256S("0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8");
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF;
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
@ -239,7 +240,7 @@ public:
}
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("00000000000000000000000000000000000000000000000004a90edff47bbdc6");
consensus.nMinimumChainWork = uint256S("000000000000000000000000000000000000000000000000098e5c63248dcb28");
/**
* The message start string should be awesome!

View File

@ -47,6 +47,7 @@
#include <rust/ed25519.h>
#include <rust/metrics.h>
#include <rust/sapling.h>
using namespace std;
@ -1317,26 +1318,18 @@ bool ContextualCheckShieldedInputs(
if (!tx.vShieldedSpend.empty() ||
!tx.vShieldedOutput.empty())
{
// The nu5Active flag passed in here enables the new consensus rules from ZIP 216
// (https://zips.z.cash/zip-0216#specification) on the following fields:
//
// - spendAuthSig in Sapling Spend descriptions
// - bindingSigSapling
auto ctx = librustzcash_sapling_verification_ctx_init(nu5Active);
auto ctx = sapling::init_verifier();
for (const SpendDescription &spend : tx.vShieldedSpend) {
if (!librustzcash_sapling_check_spend(
ctx,
spend.cv.begin(),
spend.anchor.begin(),
spend.nullifier.begin(),
spend.rk.begin(),
spend.zkproof.begin(),
spend.spendAuthSig.begin(),
dataToBeSigned.begin()
))
{
librustzcash_sapling_verification_ctx_free(ctx);
if (!ctx->check_spend(
spend.cv.GetRawBytes(),
spend.anchor.GetRawBytes(),
spend.nullifier.GetRawBytes(),
spend.rk.GetRawBytes(),
spend.zkproof,
spend.spendAuthSig,
dataToBeSigned.GetRawBytes()
)) {
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckShieldedInputs(): Sapling spend description invalid"),
@ -1345,38 +1338,30 @@ bool ContextualCheckShieldedInputs(
}
for (const OutputDescription &output : tx.vShieldedOutput) {
if (!librustzcash_sapling_check_output(
ctx,
output.cv.begin(),
output.cmu.begin(),
output.ephemeralKey.begin(),
output.zkproof.begin()
))
{
librustzcash_sapling_verification_ctx_free(ctx);
if (!ctx->check_output(
output.cv.GetRawBytes(),
output.cmu.GetRawBytes(),
output.ephemeralKey.GetRawBytes(),
output.zkproof
)) {
// This should be a non-contextual check, but we check it here
// as we need to pass over the outputs anyway in order to then
// call librustzcash_sapling_final_check().
// call ctx->final_check().
return state.DoS(100, error("ContextualCheckShieldedInputs(): Sapling output description invalid"),
REJECT_INVALID, "bad-txns-sapling-output-description-invalid");
}
}
if (!librustzcash_sapling_final_check(
ctx,
if (!ctx->final_check(
tx.GetValueBalanceSapling(),
tx.bindingSig.begin(),
dataToBeSigned.begin()
))
{
librustzcash_sapling_verification_ctx_free(ctx);
tx.bindingSig,
dataToBeSigned.GetRawBytes()
)) {
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckShieldedInputs(): Sapling binding signature invalid"),
REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid");
}
librustzcash_sapling_verification_ctx_free(ctx);
}
// Queue Orchard bundle to be batch-validated.

View File

@ -123,48 +123,6 @@ extern "C" {
/// `librustzcash_sapling_proving_ctx_init`.
void librustzcash_sapling_proving_ctx_free(void *);
/// Creates a Sapling verification context. Please free this
/// when you're done.
void * librustzcash_sapling_verification_ctx_init(
bool zip216Enabled
);
/// Check the validity of a Sapling Spend description,
/// accumulating the value commitment into the context.
bool librustzcash_sapling_check_spend(
void *ctx,
const unsigned char *cv,
const unsigned char *anchor,
const unsigned char *nullifier,
const unsigned char *rk,
const unsigned char *zkproof,
const unsigned char *spendAuthSig,
const unsigned char *sighashValue
);
/// Check the validity of a Sapling Output description,
/// accumulating the value commitment into the context.
bool librustzcash_sapling_check_output(
void *ctx,
const unsigned char *cv,
const unsigned char *cm,
const unsigned char *ephemeralKey,
const unsigned char *zkproof
);
/// Finally checks the validity of the entire Sapling
/// transaction given valueBalance and the binding signature.
bool librustzcash_sapling_final_check(
void *ctx,
int64_t valueBalance,
const unsigned char *bindingSig,
const unsigned char *sighashValue
);
/// Frees a Sapling verification context returned from
/// `librustzcash_sapling_verification_ctx_init`.
void librustzcash_sapling_verification_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::{Parameters, PreparedVerifyingKey, Proof};
use bellman::groth16::{Parameters, PreparedVerifyingKey};
use blake2s_simd::Params as Blake2sParams;
use bls12_381::Bls12;
use group::{cofactor::CofactorGroup, GroupEncoding};
@ -47,21 +47,16 @@ use zcash_primitives::{
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
merkle_tree::MerklePath,
sapling::{
self,
keys::FullViewingKey,
note_encryption::sapling_ka_agree,
redjubjub::{self, Signature},
Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ViewingKey,
keys::FullViewingKey, note_encryption::sapling_ka_agree, redjubjub, Diversifier, Note,
PaymentAddress, ProofGenerationKey, 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, SaplingVerificationContext},
sprout,
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, load_parameters,
sapling::SaplingProvingContext, sprout,
};
mod blake2b;
@ -81,6 +76,7 @@ mod init_ffi;
mod orchard_bundle;
mod orchard_ffi;
mod orchard_keys_ffi;
mod sapling;
mod transaction_ffi;
mod unified_keys_ffi;
mod wallet;
@ -537,150 +533,10 @@ pub extern "C" fn librustzcash_sapling_ka_derivepublic(
true
}
/// Creates a Sapling verification context. Please free this when you're done.
#[no_mangle]
pub extern "C" fn librustzcash_sapling_verification_ctx_init(
zip216_enabled: bool,
) -> *mut SaplingVerificationContext {
let ctx = Box::new(SaplingVerificationContext::new(zip216_enabled));
Box::into_raw(ctx)
}
/// Frees a Sapling verification context returned from
/// [`librustzcash_sapling_verification_ctx_init`].
#[no_mangle]
pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) {
drop(unsafe { Box::from_raw(ctx) });
}
const GROTH_PROOF_SIZE: usize = 48 // π_A
+ 96 // π_B
+ 48; // π_C
/// Check the validity of a Sapling Spend description, accumulating the value
/// commitment into the context.
#[no_mangle]
pub extern "C" fn librustzcash_sapling_check_spend(
ctx: *mut SaplingVerificationContext,
cv: *const [c_uchar; 32],
anchor: *const [c_uchar; 32],
nullifier: *const [c_uchar; 32],
rk: *const [c_uchar; 32],
zkproof: *const [c_uchar; GROTH_PROOF_SIZE],
spend_auth_sig: *const [c_uchar; 64],
sighash_value: *const [c_uchar; 32],
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*cv })) {
Some(p) => p,
None => return false,
};
// Deserialize the anchor, which should be an element
// of Fr.
let anchor = match de_ct(bls12_381::Scalar::from_bytes(unsafe { &*anchor })) {
Some(a) => a,
None => return false,
};
// Deserialize rk
let rk = match redjubjub::PublicKey::read(&(unsafe { &*rk })[..]) {
Ok(p) => p,
Err(_) => return false,
};
// Deserialize the signature
let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
// Deserialize the proof
let zkproof = match Proof::read(&(unsafe { &*zkproof })[..]) {
Ok(p) => p,
Err(_) => return false,
};
unsafe { &mut *ctx }.check_spend(
cv,
anchor,
unsafe { &*nullifier },
rk,
unsafe { &*sighash_value },
spend_auth_sig,
zkproof,
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
)
}
/// Check the validity of a Sapling Output description, accumulating the value
/// commitment into the context.
#[no_mangle]
pub extern "C" fn librustzcash_sapling_check_output(
ctx: *mut SaplingVerificationContext,
cv: *const [c_uchar; 32],
cm: *const [c_uchar; 32],
epk: *const [c_uchar; 32],
zkproof: *const [c_uchar; GROTH_PROOF_SIZE],
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*cv })) {
Some(p) => p,
None => return false,
};
// Deserialize the commitment, which should be an element
// of Fr.
let cm = match de_ct(bls12_381::Scalar::from_bytes(unsafe { &*cm })) {
Some(a) => a,
None => return false,
};
// Deserialize the ephemeral key
let epk = match de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*epk })) {
Some(p) => p,
None => return false,
};
// Deserialize the proof
let zkproof = match Proof::read(&(unsafe { &*zkproof })[..]) {
Ok(p) => p,
Err(_) => return false,
};
unsafe { &mut *ctx }.check_output(
cv,
cm,
epk,
zkproof,
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
)
}
/// Finally checks the validity of the entire Sapling transaction given
/// valueBalance and the binding signature.
#[no_mangle]
pub extern "C" fn librustzcash_sapling_final_check(
ctx: *mut SaplingVerificationContext,
value_balance: i64,
binding_sig: *const [c_uchar; 64],
sighash_value: *const [c_uchar; 32],
) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
};
// Deserialize the signature
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
unsafe { &*ctx }.final_check(value_balance, unsafe { &*sighash_value }, binding_sig)
}
/// Sprout JoinSplit proof generation.
#[no_mangle]
pub extern "C" fn librustzcash_sprout_prove(
@ -1170,7 +1026,7 @@ pub extern "C" fn librustzcash_sapling_diversifier_index(
j_ret: *mut [c_uchar; 11],
) {
let dk = zip32::DiversifierKey(unsafe { *dk });
let diversifier = sapling::Diversifier(unsafe { *d });
let diversifier = Diversifier(unsafe { *d });
let j_ret = unsafe { &mut *j_ret };
let j = dk.diversifier_index(&diversifier);

176
src/rust/src/sapling.rs Normal file
View File

@ -0,0 +1,176 @@
// Copyright (c) 2020-2022 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
// This is added because `check_spend` takes several arguments over FFI. This
// annotation gets removed by the cxx procedural macro so it needs to be enabled
// on the entire module.
#![allow(clippy::too_many_arguments)]
use bellman::groth16::Proof;
use group::GroupEncoding;
use zcash_primitives::{
sapling::redjubjub::{self, Signature},
transaction::components::Amount,
};
use zcash_proofs::sapling::SaplingVerificationContext;
use super::GROTH_PROOF_SIZE;
use super::{de_ct, SAPLING_OUTPUT_VK, SAPLING_SPEND_VK};
#[cxx::bridge(namespace = "sapling")]
mod ffi {
extern "Rust" {
type Verifier;
fn init_verifier() -> Box<Verifier>;
fn check_spend(
&mut self,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: &[u8; 32],
rk: &[u8; 32],
zkproof: &[u8; 192], // GROTH_PROOF_SIZE
spend_auth_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool;
fn check_output(
&mut self,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: &[u8; 32],
zkproof: &[u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn final_check(
&self,
value_balance: i64,
binding_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool;
}
}
struct Verifier(SaplingVerificationContext);
fn init_verifier() -> Box<Verifier> {
// We consider ZIP 216 active all of the time because blocks prior to NU5
// activation (on mainnet and testnet) did not contain Sapling transactions
// that violated its canonicity rule.
Box::new(Verifier(SaplingVerificationContext::new(true)))
}
impl Verifier {
fn check_spend(
&mut self,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: &[u8; 32],
rk: &[u8; 32],
zkproof: &[u8; GROTH_PROOF_SIZE],
spend_auth_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
Some(p) => p,
None => return false,
};
// Deserialize the anchor, which should be an element
// of Fr.
let anchor = match de_ct(bls12_381::Scalar::from_bytes(anchor)) {
Some(a) => a,
None => return false,
};
// Deserialize rk
let rk = match redjubjub::PublicKey::read(&rk[..]) {
Ok(p) => p,
Err(_) => return false,
};
// Deserialize the signature
let spend_auth_sig = match Signature::read(&spend_auth_sig[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
// Deserialize the proof
let zkproof = match Proof::read(&zkproof[..]) {
Ok(p) => p,
Err(_) => return false,
};
self.0.check_spend(
cv,
anchor,
nullifier,
rk,
sighash_value,
spend_auth_sig,
zkproof,
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
)
}
fn check_output(
&mut self,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: &[u8; 32],
zkproof: &[u8; GROTH_PROOF_SIZE],
) -> bool {
// Deserialize the value commitment
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
Some(p) => p,
None => return false,
};
// Deserialize the commitment, which should be an element
// of Fr.
let cm = match de_ct(bls12_381::Scalar::from_bytes(cm)) {
Some(a) => a,
None => return false,
};
// Deserialize the ephemeral key
let ephemeral_key = match de_ct(jubjub::ExtendedPoint::from_bytes(ephemeral_key)) {
Some(p) => p,
None => return false,
};
// Deserialize the proof
let zkproof = match Proof::read(&zkproof[..]) {
Ok(p) => p,
Err(_) => return false,
};
self.0.check_output(
cv,
cm,
ephemeral_key,
zkproof,
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
)
}
fn final_check(
&self,
value_balance: i64,
binding_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
};
// Deserialize the signature
let binding_sig = match Signature::read(&binding_sig[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
self.0
.final_check(value_balance, sighash_value, binding_sig)
}
}

View File

@ -89,6 +89,14 @@ public:
((uint64_t)ptr[7]) << 56;
}
std::array<uint8_t, WIDTH> GetRawBytes() const
{
std::array<uint8_t, WIDTH> buf = {};
memcpy(buf.data(), this->begin(), WIDTH);
return buf;
}
template<typename Stream>
void Serialize(Stream& s) const
{

View File

@ -36,6 +36,7 @@
#include "librustzcash.h"
#include <rust/ed25519/types.h>
#include <rust/sapling.h>
using namespace libzcash;
// This method is based on Shutdown from init.cpp
@ -797,24 +798,22 @@ double benchmark_verify_sapling_spend()
ss >> spend;
uint256 dataToBeSigned = uint256S("0x2dbf83fe7b88a7cbd80fac0c719483906bb9a0c4fc69071e4780d5f2c76e592c");
auto ctx = librustzcash_sapling_verification_ctx_init(true);
auto ctx = sapling::init_verifier();
struct timeval tv_start;
timer_start(tv_start);
bool result = librustzcash_sapling_check_spend(
ctx,
spend.cv.begin(),
spend.anchor.begin(),
spend.nullifier.begin(),
spend.rk.begin(),
spend.zkproof.begin(),
spend.spendAuthSig.begin(),
dataToBeSigned.begin()
);
bool result = ctx->check_spend(
spend.cv.GetRawBytes(),
spend.anchor.GetRawBytes(),
spend.nullifier.GetRawBytes(),
spend.rk.GetRawBytes(),
spend.zkproof,
spend.spendAuthSig,
dataToBeSigned.GetRawBytes()
);
double t = timer_stop(tv_start);
librustzcash_sapling_verification_ctx_free(ctx);
if (!result) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_check_spend() should return true");
}
@ -830,21 +829,19 @@ double benchmark_verify_sapling_output()
CDataStream ss(ParseHex("edd742af18857e5ec2d71d346a7fe2ac97c137339bd5268eea86d32e0ff4f38f76213fa8cfed3347ac4e8572dd88aff395c0c10a59f8b3f49d2bc539ed6c726667e29d4763f914ddd0abf1cdfa84e44de87c233434c7e69b8b5b8f4623c8aa444163425bae5cef842972fed66046c1c6ce65c866ad894d02e6e6dcaae7a962d9f2ef95757a09c486928e61f0f7aed90ad0a542b0d3dc5fe140dfa7626b9315c77e03b055f19cbacd21a866e46f06c00e0c7792b2a590a611439b510a9aaffcf1073bad23e712a9268b36888e3727033eee2ab4d869f54a843f93b36ef489fb177bf74b41a9644e5d2a0a417c6ac1c8869bc9b83273d453f878ed6fd96b82a5939903f7b64ecaf68ea16e255a7fb7cc0b6d8b5608a1c6b0ed3024cc62c2f0f9c5cfc7b431ae6e9d40815557aa1d010523f9e1960de77b2274cb6710d229d475c87ae900183206ba90cb5bbc8ec0df98341b82726c705e0308ca5dc08db4db609993a1046dfb43dfd8c760be506c0bed799bb2205fc29dc2e654dce731034a23b0aaf6da0199248702ee0523c159f41f4cbfff6c35ace4dd9ae834e44e09c76a0cbdda1d3f6a2c75ad71212daf9575ab5f09ca148718e667f29ddf18c8a330a86ace18a86e89454653902aa393c84c6b694f27d0d42e24e7ac9fe34733de5ec15f5066081ce912c62c1a804a2bb4dedcef7cc80274f6bb9e89e2fce91dc50d6a73c8aefb9872f1cf3524a92626a0b8f39bbf7bf7d96ca2f770fc04d7f457021c536a506a187a93b2245471ddbfb254a71bc4a0d72c8d639a31c7b1920087ffca05c24214157e2e7b28184e91989ef0b14f9b34c3dc3cc0ac64226b9e337095870cb0885737992e120346e630a416a9b217679ce5a778fb15779c136bcecca5efe79012013d77d90b4e99dd22c8f35bc77121716e160d05bd30d288ee8886390ee436f85bdc9029df888a3a3326d9d4ddba5cb5318b3274928829d662e96fea1d601f7a306251ed8c6cc4e5a3a7a98c35a3650482a0eee08f3b4c2da9b22947c96138f1505c2f081f8972d429f3871f32bef4aaa51aa6945df8e9c9760531ac6f627d17c1518202818a91ca304fb4037875c666060597976144fcbbc48a776a2c61beb9515fa8f3ae6d3a041d320a38a8ac75cb47bb9c866ee497fc3cd13299970c4b369c1c2ceb4220af082fbecdd8114492a8e4d713b5a73396fd224b36c1185bd5e20d683e6c8db35346c47ae7401988255da7cfffdced5801067d4d296688ee8fe424b4a8a69309ce257eefb9345ebfda3f6de46bb11ec94133e1f72cd7ac54934d6cf17b3440800e70b80ebc7c7bfc6fb0fc2c"), SER_NETWORK, PROTOCOL_VERSION);
ss >> output;
auto ctx = librustzcash_sapling_verification_ctx_init(true);
auto ctx = sapling::init_verifier();
struct timeval tv_start;
timer_start(tv_start);
bool result = librustzcash_sapling_check_output(
ctx,
output.cv.begin(),
output.cmu.begin(),
output.ephemeralKey.begin(),
output.zkproof.begin()
);
bool result = ctx->check_output(
output.cv.GetRawBytes(),
output.cmu.GetRawBytes(),
output.ephemeralKey.GetRawBytes(),
output.zkproof
);
double t = timer_stop(tv_start);
librustzcash_sapling_verification_ctx_free(ctx);
if (!result) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "librustzcash_sapling_check_output() should return true");
}