diff --git a/src/Makefile.am b/src/Makefile.am index 7a878a430de..7ae81732cbb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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. diff --git a/src/bench/verification.cpp b/src/bench/verification.cpp index 1b5300a40b4..705bb2ace48 100644 --- a/src/bench/verification.cpp +++ b/src/bench/verification.cpp @@ -16,6 +16,7 @@ #include "librustzcash.h" #include +#include 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); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 91166d93f09..c6a74103d40 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -135,7 +135,8 @@ class CMainParams : public CChainParams { 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 @@ class CMainParams : public CChainParams { } // 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! ⓩ❤ diff --git a/src/main.cpp b/src/main.cpp index 86b826e7800..2e35d00e37b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ #include #include +#include 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. diff --git a/src/rust/include/librustzcash.h b/src/rust/include/librustzcash.h index beab63614a1..7852c470640 100644 --- a/src/rust/include/librustzcash.h +++ b/src/rust/include/librustzcash.h @@ -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. diff --git a/src/rust/src/rustzcash.rs b/src/rust/src/rustzcash.rs index 1cd2289d355..9783c493959 100644 --- a/src/rust/src/rustzcash.rs +++ b/src/rust/src/rustzcash.rs @@ -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); diff --git a/src/rust/src/sapling.rs b/src/rust/src/sapling.rs new file mode 100644 index 00000000000..79955a053e7 --- /dev/null +++ b/src/rust/src/sapling.rs @@ -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; + 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 { + // 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) + } +} diff --git a/src/uint256.h b/src/uint256.h index 24a1f342679..a8c9bbe5c76 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -89,6 +89,14 @@ class base_blob ((uint64_t)ptr[7]) << 56; } + std::array GetRawBytes() const + { + std::array buf = {}; + memcpy(buf.data(), this->begin(), WIDTH); + + return buf; + } + template void Serialize(Stream& s) const { diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 0c85e00d70e..be7de3f1b5d 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -36,6 +36,7 @@ #include "librustzcash.h" #include +#include 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"); }