Merge most `cxx::bridge` definitions into a single bridge

This enables us to use Rust types across more bridged APIs, which we
can't do with multiple bridge definitions until `cxx` adds support.
This commit is contained in:
Jack Grigg 2023-01-24 01:07:29 +00:00 committed by Jack Grigg
parent 1feec6a3b4
commit 33367709f7
25 changed files with 388 additions and 384 deletions

View File

@ -50,34 +50,21 @@ if ENABLE_WALLET
LIBBITCOIN_WALLET=libbitcoin_wallet.a
endif
# TODO: Figure out how to avoid an explicit file list.
CXXBRIDGE_RS = \
rust/src/blake2b.rs \
rust/src/bundlecache.rs \
rust/src/equihash.rs \
rust/src/merkle_frontier.rs \
rust/src/orchard_bundle.rs \
rust/src/sapling.rs \
rust/src/streams.rs \
rust/src/wallet_scanner.rs
rust/src/bridge.rs
CXXBRIDGE_H = \
rust/gen/include/rust/blake2b.h \
rust/gen/include/rust/bundlecache.h \
rust/gen/include/rust/equihash.h \
rust/gen/include/rust/merkle_frontier.h \
rust/gen/include/rust/orchard_bundle.h \
rust/gen/include/rust/sapling.h \
rust/gen/include/rust/streams.h \
rust/gen/include/rust/wallet_scanner.h
rust/gen/include/rust/bridge.h
CXXBRIDGE_CPP = \
rust/gen/src/blake2b.cpp \
rust/gen/src/bundlecache.cpp \
rust/gen/src/equihash.cpp \
rust/gen/src/merkle_frontier.cpp \
rust/gen/src/orchard_bundle.cpp \
rust/gen/src/sapling.cpp \
rust/gen/src/streams.cpp \
rust/gen/src/wallet_scanner.cpp
rust/gen/src/bridge.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

@ -15,8 +15,8 @@
#include "version.h"
#include "librustzcash.h"
#include <rust/bridge.h>
#include <rust/ed25519.h>
#include <rust/sapling.h>
static void ECDSA(benchmark::State& state)
{

View File

@ -14,7 +14,7 @@
#include <vector>
#include <rust/wallet_scanner.h>
#include <rust/bridge.h>
struct CDNSSeedData {
std::string name, host;

View File

@ -11,7 +11,7 @@
#include <sodium.h>
#include <tracing.h>
#include <rust/bundlecache.h>
#include <rust/bridge.h>
#include <boost/filesystem.hpp>

View File

@ -11,8 +11,8 @@
#include "zcash/JoinSplit.hpp"
#include <librustzcash.h>
#include <rust/bridge.h>
#include <rust/ed25519.h>
#include <rust/sapling.h>
#include <rust/orchard.h>
// Subclass of CTransaction which doesn't call UpdateHash when constructing

View File

@ -67,7 +67,7 @@
#include "zmq/zmqnotificationinterface.h"
#endif
#include <rust/bundlecache.h>
#include <rust/bridge.h>
#include <rust/init.h>
#include <rust/metrics.h>

View File

@ -43,7 +43,7 @@
#include <utility>
#include <vector>
#include <rust/sapling.h>
#include <rust/bridge.h>
#include <rust/orchard.h>
#include <boost/unordered_map.hpp>

View File

@ -37,7 +37,7 @@
#include "validationinterface.h"
#include <librustzcash.h>
#include <rust/sapling.h>
#include <rust/bridge.h>
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>

View File

@ -8,8 +8,9 @@
#include "streams.h"
#include <amount.h>
#include <rust/bridge.h>
#include <rust/orchard.h>
#include <rust/orchard_bundle.h>
#include <rust/orchard/wallet.h>
#include "zcash/address/orchard.hpp"

View File

@ -32,7 +32,7 @@
#include <boost/assign/list_of.hpp>
#include <univalue.h>
#include <rust/orchard_bundle.h>
#include <rust/bridge.h>
using namespace std;

277
src/rust/src/bridge.rs Normal file
View File

@ -0,0 +1,277 @@
//! FFI bridges between `zcashd`'s Rust and C++ code.
//!
//! These are all collected into a single file because we can't use the same Rust type
//! across multiple bridges until https://github.com/dtolnay/cxx/issues/496 is closed.
//!
//! The bridges that we leave separate are either standalone Rust code, or exporting C++
//! types to Rust (because we _can_ use the same C++ type across multiple bridges).
use crate::{
bundlecache::init as bundlecache_init,
merkle_frontier::{new_orchard, orchard_empty_root, parse_orchard, Orchard, OrchardWallet},
note_encryption::{
try_sapling_note_decryption, try_sapling_output_recovery, DecryptedSaplingOutput,
},
orchard_bundle::{from_tx_bundle, Action, Bundle, OrchardBundle},
params::{network, Network},
sapling::{
finish_bundle_assembly, init_batch_validator, init_prover, init_verifier,
new_bundle_assembler, BatchValidator, Bundle as SaplingBundle,
BundleAssembler as SaplingBundleAssembler, Prover, Verifier,
},
wallet_scanner::{init_batch_scanner, BatchResult, BatchScanner},
};
#[cxx::bridge]
pub(crate) mod ffi {
extern "C++" {
include!("streams.h");
#[cxx_name = "RustDataStream"]
type RustStream = crate::streams::ffi::RustStream;
}
#[namespace = "consensus"]
extern "Rust" {
type Network;
fn network(
network: &str,
overwinter: i32,
sapling: i32,
blossom: i32,
heartwood: i32,
canopy: i32,
nu5: i32,
) -> Result<Box<Network>>;
}
#[namespace = "libzcash"]
unsafe extern "C++" {
include!("zcash/cache.h");
type BundleValidityCache;
fn NewBundleValidityCache(kind: &str, bytes: usize) -> UniquePtr<BundleValidityCache>;
fn insert(self: Pin<&mut BundleValidityCache>, entry: [u8; 32]);
fn contains(&self, entry: &[u8; 32], erase: bool) -> bool;
}
#[namespace = "bundlecache"]
extern "Rust" {
#[rust_name = "bundlecache_init"]
fn init(cache_bytes: usize);
}
#[namespace = "sapling"]
extern "Rust" {
#[rust_name = "SaplingBundle"]
type Bundle;
#[rust_name = "SaplingBundleAssembler"]
type BundleAssembler;
fn new_bundle_assembler() -> Box<SaplingBundleAssembler>;
fn add_spend(
self: &mut SaplingBundleAssembler,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: [u8; 32],
rk: &[u8; 32],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
spend_auth_sig: &[u8; 64],
) -> bool;
fn add_output(
self: &mut SaplingBundleAssembler,
cv: &[u8; 32],
cmu: &[u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn finish_bundle_assembly(
assembler: Box<SaplingBundleAssembler>,
value_balance: i64,
binding_sig: [u8; 64],
) -> Box<SaplingBundle>;
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>;
#[allow(clippy::too_many_arguments)]
fn check_spend(
self: &mut Verifier,
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(
self: &mut Verifier,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: &[u8; 32],
zkproof: &[u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn final_check(
self: &Verifier,
value_balance: i64,
binding_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool;
type BatchValidator;
fn init_batch_validator(cache_store: bool) -> Box<BatchValidator>;
fn check_bundle(
self: &mut BatchValidator,
bundle: Box<SaplingBundle>,
sighash: [u8; 32],
) -> bool;
fn validate(self: &mut BatchValidator) -> bool;
}
#[namespace = "orchard_bundle"]
extern "Rust" {
type Action;
type Bundle;
type OrchardBundle;
fn cv(self: &Action) -> [u8; 32];
fn nullifier(self: &Action) -> [u8; 32];
fn rk(self: &Action) -> [u8; 32];
fn cmx(self: &Action) -> [u8; 32];
fn ephemeral_key(self: &Action) -> [u8; 32];
fn enc_ciphertext(self: &Action) -> [u8; 580];
fn out_ciphertext(self: &Action) -> [u8; 80];
fn spend_auth_sig(self: &Action) -> [u8; 64];
unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box<Bundle>;
fn actions(self: &Bundle) -> Vec<Action>;
fn num_actions(self: &Bundle) -> usize;
fn enable_spends(self: &Bundle) -> bool;
fn enable_outputs(self: &Bundle) -> bool;
fn value_balance_zat(self: &Bundle) -> i64;
fn anchor(self: &Bundle) -> [u8; 32];
fn proof(self: &Bundle) -> Vec<u8>;
fn binding_sig(self: &Bundle) -> [u8; 64];
}
#[namespace = "merkle_frontier"]
extern "Rust" {
type Orchard;
type OrchardWallet;
fn orchard_empty_root() -> [u8; 32];
fn new_orchard() -> Box<Orchard>;
fn box_clone(self: &Orchard) -> Box<Orchard>;
fn parse_orchard(stream: Pin<&mut RustStream>) -> Result<Box<Orchard>>;
fn serialize(self: &Orchard, stream: Pin<&mut RustStream>) -> Result<()>;
fn serialize_legacy(self: &Orchard, stream: Pin<&mut RustStream>) -> Result<()>;
fn dynamic_memory_usage(self: &Orchard) -> usize;
fn root(self: &Orchard) -> [u8; 32];
fn size(self: &Orchard) -> u64;
fn append_bundle(self: &mut Orchard, bundle: &Bundle) -> bool;
unsafe fn init_wallet(self: &Orchard, wallet: *mut OrchardWallet) -> bool;
}
#[namespace = "wallet"]
struct SaplingDecryptionResult {
txid: [u8; 32],
output: u32,
ivk: [u8; 32],
diversifier: [u8; 11],
pk_d: [u8; 32],
}
#[namespace = "wallet"]
pub(crate) struct SaplingShieldedOutput {
cv: [u8; 32],
cmu: [u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
}
#[namespace = "wallet"]
extern "Rust" {
fn try_sapling_note_decryption(
network: &Network,
height: u32,
raw_ivk: &[u8; 32],
output: SaplingShieldedOutput,
) -> Result<Box<DecryptedSaplingOutput>>;
fn try_sapling_output_recovery(
network: &Network,
height: u32,
ovk: [u8; 32],
output: SaplingShieldedOutput,
) -> Result<Box<DecryptedSaplingOutput>>;
type DecryptedSaplingOutput;
fn note_value(self: &DecryptedSaplingOutput) -> u64;
fn note_rseed(self: &DecryptedSaplingOutput) -> [u8; 32];
fn zip_212_enabled(self: &DecryptedSaplingOutput) -> bool;
fn recipient_d(self: &DecryptedSaplingOutput) -> [u8; 11];
fn recipient_pk_d(self: &DecryptedSaplingOutput) -> [u8; 32];
fn memo(self: &DecryptedSaplingOutput) -> [u8; 512];
type BatchScanner;
type BatchResult;
fn init_batch_scanner(
network: &Network,
sapling_ivks: &[[u8; 32]],
) -> Result<Box<BatchScanner>>;
fn add_transaction(
self: &mut BatchScanner,
block_tag: [u8; 32],
tx_bytes: &[u8],
height: u32,
) -> Result<()>;
fn flush(self: &mut BatchScanner);
fn collect_results(
self: &mut BatchScanner,
block_tag: [u8; 32],
txid: [u8; 32],
) -> Box<BatchResult>;
fn get_sapling(self: &BatchResult) -> Vec<SaplingDecryptionResult>;
}
}

View File

@ -5,23 +5,7 @@ use std::{
use rand_core::{OsRng, RngCore};
#[cxx::bridge]
mod ffi {
#[namespace = "libzcash"]
unsafe extern "C++" {
include!("zcash/cache.h");
type BundleValidityCache;
fn NewBundleValidityCache(kind: &str, bytes: usize) -> UniquePtr<BundleValidityCache>;
fn insert(self: Pin<&mut BundleValidityCache>, entry: [u8; 32]);
fn contains(&self, entry: &[u8; 32], erase: bool) -> bool;
}
#[namespace = "bundlecache"]
extern "Rust" {
fn init(cache_bytes: usize);
}
}
use crate::bridge::ffi;
pub(crate) struct CacheEntry([u8; 32]);
@ -114,7 +98,7 @@ static BUNDLE_CACHES_LOADED: Once = Once::new();
static mut SAPLING_BUNDLE_VALIDITY_CACHE: Option<RwLock<BundleValidityCache>> = None;
static mut ORCHARD_BUNDLE_VALIDITY_CACHE: Option<RwLock<BundleValidityCache>> = None;
fn init(cache_bytes: usize) {
pub(crate) fn init(cache_bytes: usize) {
BUNDLE_CACHES_LOADED.call_once(|| unsafe {
SAPLING_BUNDLE_VALIDITY_CACHE = Some(RwLock::new(BundleValidityCache::new(
"Sapling",

View File

@ -2,63 +2,31 @@ use core::mem::size_of_val;
use core::pin::Pin;
use incrementalmerkletree::{bridgetree, Altitude, Frontier, Hashable};
use orchard::{bundle::Authorized, tree::MerkleHashOrchard};
use orchard::tree::MerkleHashOrchard;
use tracing::error;
use zcash_primitives::{
merkle_tree::{
incremental::{read_frontier_v1, write_frontier_v1},
CommitmentTree, HashSer,
},
transaction::components::Amount,
use zcash_primitives::merkle_tree::{
incremental::{read_frontier_v1, write_frontier_v1},
CommitmentTree, HashSer,
};
use crate::{streams::CppStream, wallet::Wallet};
use crate::{bridge::ffi, orchard_bundle, streams::CppStream, wallet::Wallet};
pub const MERKLE_DEPTH: u8 = 32;
#[cxx::bridge]
mod ffi {
extern "C++" {
include!("streams.h");
#[cxx_name = "RustDataStream"]
type RustStream = crate::streams::ffi::RustStream;
}
#[namespace = "merkle_frontier"]
extern "Rust" {
type Orchard;
type OrchardBundle;
type OrchardWallet;
fn orchard_empty_root() -> [u8; 32];
fn new_orchard() -> Box<Orchard>;
fn box_clone(self: &Orchard) -> Box<Orchard>;
fn parse_orchard(stream: Pin<&mut RustStream>) -> Result<Box<Orchard>>;
fn serialize(self: &Orchard, stream: Pin<&mut RustStream>) -> Result<()>;
fn serialize_legacy(self: &Orchard, stream: Pin<&mut RustStream>) -> Result<()>;
fn dynamic_memory_usage(self: &Orchard) -> usize;
fn root(self: &Orchard) -> [u8; 32];
fn size(self: &Orchard) -> u64;
unsafe fn append_bundle(self: &mut Orchard, bundle: *const OrchardBundle) -> bool;
unsafe fn init_wallet(self: &Orchard, wallet: *mut OrchardWallet) -> bool;
}
}
type Inner<H> = bridgetree::Frontier<H, MERKLE_DEPTH>;
/// An incremental Merkle frontier.
#[derive(Clone)]
struct MerkleFrontier<H>(Inner<H>);
pub(crate) struct MerkleFrontier<H>(Inner<H>);
impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
/// Returns a copy of the value.
fn box_clone(&self) -> Box<Self> {
pub(crate) fn box_clone(&self) -> Box<Self> {
Box::new(self.clone())
}
/// Attempts to parse a Merkle frontier from the given C++ stream.
fn parse(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Self>, String> {
pub(crate) fn parse(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Self>, String> {
let reader = CppStream::from(stream);
match read_frontier_v1(reader) {
@ -68,14 +36,14 @@ impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
}
/// Serializes the frontier to the given C++ stream.
fn serialize(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
pub(crate) fn serialize(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
let writer = CppStream::from(stream);
write_frontier_v1(writer, &self.0)
.map_err(|e| format!("Failed to serialize v5 Merkle frontier: {}", e))
}
/// Serializes the frontier to the given C++ stream in the legacy frontier encoding.
fn serialize_legacy(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
pub(crate) fn serialize_legacy(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
let writer = CppStream::from(stream);
let commitment_tree = CommitmentTree::from_frontier(&self.0);
commitment_tree.write(writer).map_err(|e| {
@ -89,13 +57,13 @@ impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
/// Returns the amount of memory dynamically allocated for the frontier.
///
/// Includes `self` because this type is stored on the heap when passed to C++.
fn dynamic_memory_usage(&self) -> usize {
pub(crate) fn dynamic_memory_usage(&self) -> usize {
size_of_val(&self.0) + self.0.dynamic_memory_usage()
}
/// Obtains the current root of this Merkle frontier by hashing against empty nodes up
/// to the maximum height of the pruned tree that the frontier represents.
fn root(&self) -> [u8; 32] {
pub(crate) fn root(&self) -> [u8; 32] {
let mut root = [0; 32];
self.0
.root()
@ -105,39 +73,36 @@ impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
}
/// Returns the number of leaves appended to the frontier.
fn size(&self) -> u64 {
pub(crate) fn size(&self) -> u64 {
self.0.position().map_or(0, |p| <u64>::from(p) + 1)
}
}
/// Returns the root of an empty Orchard Merkle tree.
fn orchard_empty_root() -> [u8; 32] {
pub(crate) fn orchard_empty_root() -> [u8; 32] {
let altitude = Altitude::from(MERKLE_DEPTH);
MerkleHashOrchard::empty_root(altitude).to_bytes()
}
/// An Orchard incremental Merkle frontier.
type Orchard = MerkleFrontier<MerkleHashOrchard>;
pub(crate) type Orchard = MerkleFrontier<MerkleHashOrchard>;
/// Constructs a new empty Orchard Merkle frontier.
fn new_orchard() -> Box<Orchard> {
pub(crate) fn new_orchard() -> Box<Orchard> {
Box::new(MerkleFrontier(Inner::empty()))
}
/// Attempts to parse an Orchard Merkle frontier from the given C++ stream.
fn parse_orchard(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Orchard>, String> {
pub(crate) fn parse_orchard(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Orchard>, String> {
Orchard::parse(stream)
}
struct OrchardBundle;
struct OrchardWallet;
pub(crate) struct OrchardWallet;
impl Orchard {
/// Appends the note commitments in the given bundle to this frontier.
fn append_bundle(&mut self, bundle: *const OrchardBundle) -> bool {
let bundle = unsafe { (bundle as *const orchard::Bundle<Authorized, Amount>).as_ref() };
if let Some(bundle) = bundle {
pub(crate) fn append_bundle(&mut self, bundle: &orchard_bundle::Bundle) -> bool {
if let Some(bundle) = bundle.inner() {
for action in bundle.actions().iter() {
if !self.0.append(&MerkleHashOrchard::from_cmx(action.cmx())) {
error!("Orchard note commitment tree is full.");
@ -155,7 +120,7 @@ impl Orchard {
/// This will fail with an assertion error if any checkpoints exist in the tree.
///
/// TODO: Remove once `crate::wallet` is migrated to `cxx`.
fn init_wallet(&self, wallet: *mut OrchardWallet) -> bool {
pub(crate) fn init_wallet(&self, wallet: *mut OrchardWallet) -> bool {
crate::wallet::orchard_wallet_init_from_frontier(wallet as *mut Wallet, &self.0)
}
}

View File

@ -15,7 +15,7 @@ use zcash_primitives::{
},
};
use crate::{params::Network, wallet_scanner::ffi::SaplingShieldedOutput};
use crate::{bridge::ffi::SaplingShieldedOutput, params::Network};
/// Trial decryption of the full note plaintext by the recipient.
///

View File

@ -4,80 +4,56 @@ use orchard::{
};
use zcash_primitives::transaction::components::Amount;
#[cxx::bridge(namespace = "orchard_bundle")]
mod ffi {
extern "Rust" {
type Action;
type Bundle;
type OrchardBundle;
fn cv(self: &Action) -> [u8; 32];
fn nullifier(self: &Action) -> [u8; 32];
fn rk(self: &Action) -> [u8; 32];
fn cmx(self: &Action) -> [u8; 32];
fn ephemeral_key(self: &Action) -> [u8; 32];
fn enc_ciphertext(self: &Action) -> [u8; 580];
fn out_ciphertext(self: &Action) -> [u8; 80];
fn spend_auth_sig(self: &Action) -> [u8; 64];
unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box<Bundle>;
fn actions(self: &Bundle) -> Vec<Action>;
fn num_actions(self: &Bundle) -> usize;
fn enable_spends(self: &Bundle) -> bool;
fn enable_outputs(self: &Bundle) -> bool;
fn value_balance_zat(self: &Bundle) -> i64;
fn anchor(self: &Bundle) -> [u8; 32];
fn proof(self: &Bundle) -> Vec<u8>;
fn binding_sig(self: &Bundle) -> [u8; 64];
}
}
pub struct Action(orchard::Action<Signature<SpendAuth>>);
impl Action {
fn cv(&self) -> [u8; 32] {
pub(crate) fn cv(&self) -> [u8; 32] {
self.0.cv_net().to_bytes()
}
fn nullifier(&self) -> [u8; 32] {
pub(crate) fn nullifier(&self) -> [u8; 32] {
self.0.nullifier().to_bytes()
}
fn rk(&self) -> [u8; 32] {
pub(crate) fn rk(&self) -> [u8; 32] {
self.0.rk().into()
}
fn cmx(&self) -> [u8; 32] {
pub(crate) fn cmx(&self) -> [u8; 32] {
self.0.cmx().to_bytes()
}
fn ephemeral_key(&self) -> [u8; 32] {
pub(crate) fn ephemeral_key(&self) -> [u8; 32] {
self.0.encrypted_note().epk_bytes
}
fn enc_ciphertext(&self) -> [u8; 580] {
pub(crate) fn enc_ciphertext(&self) -> [u8; 580] {
self.0.encrypted_note().enc_ciphertext
}
fn out_ciphertext(&self) -> [u8; 80] {
pub(crate) fn out_ciphertext(&self) -> [u8; 80] {
self.0.encrypted_note().out_ciphertext
}
fn spend_auth_sig(&self) -> [u8; 64] {
pub(crate) fn spend_auth_sig(&self) -> [u8; 64] {
self.0.authorization().into()
}
}
pub struct Bundle(Option<orchard::Bundle<Authorized, Amount>>);
pub struct OrchardBundle;
unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box<Bundle> {
pub(crate) unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box<Bundle> {
Box::new(Bundle(
{ (bundle as *const orchard::Bundle<Authorized, Amount>).as_ref() }.cloned(),
))
}
impl Bundle {
fn actions(&self) -> Vec<Action> {
pub(crate) fn inner(&self) -> Option<&orchard::Bundle<Authorized, Amount>> {
self.0.as_ref()
}
pub(crate) fn actions(&self) -> Vec<Action> {
self.0
.iter()
.flat_map(|b| b.actions().iter())
@ -86,42 +62,35 @@ impl Bundle {
.collect()
}
fn num_actions(&self) -> usize {
self.0.as_ref().map(|b| b.actions().len()).unwrap_or(0)
pub(crate) fn num_actions(&self) -> usize {
self.inner().map(|b| b.actions().len()).unwrap_or(0)
}
fn enable_spends(&self) -> bool {
self.0
.as_ref()
pub(crate) fn enable_spends(&self) -> bool {
self.inner()
.map(|b| b.flags().spends_enabled())
.unwrap_or(false)
}
fn enable_outputs(&self) -> bool {
self.0
.as_ref()
pub(crate) fn enable_outputs(&self) -> bool {
self.inner()
.map(|b| b.flags().outputs_enabled())
.unwrap_or(false)
}
fn value_balance_zat(&self) -> i64 {
self.0
.as_ref()
.map(|b| b.value_balance().into())
.unwrap_or(0)
pub(crate) fn value_balance_zat(&self) -> i64 {
self.inner().map(|b| b.value_balance().into()).unwrap_or(0)
}
fn anchor(&self) -> [u8; 32] {
self.0
.as_ref()
pub(crate) fn anchor(&self) -> [u8; 32] {
self.inner()
.expect("Bundle actions should have been checked to be non-empty")
.anchor()
.to_bytes()
}
fn proof(&self) -> Vec<u8> {
self.0
.as_ref()
pub(crate) fn proof(&self) -> Vec<u8> {
self.inner()
.expect("Bundle actions should have been checked to be non-empty")
.authorization()
.proof()
@ -129,9 +98,8 @@ impl Bundle {
.to_vec()
}
fn binding_sig(&self) -> [u8; 64] {
self.0
.as_ref()
pub(crate) fn binding_sig(&self) -> [u8; 64] {
self.inner()
.expect("Bundle actions should have been checked to be non-empty")
.authorization()
.binding_signature()

View File

@ -70,6 +70,8 @@ mod streams_ffi;
mod tracing_ffi;
mod zcashd_orchard;
mod bridge;
mod address_ffi;
mod builder_ffi;
mod bundlecache;

View File

@ -36,106 +36,7 @@ use crate::bundlecache::{
sapling_bundle_validity_cache, sapling_bundle_validity_cache_mut, CacheEntries,
};
#[cxx::bridge(namespace = "sapling")]
mod ffi {
extern "Rust" {
type Bundle;
type BundleAssembler;
fn new_bundle_assembler() -> Box<BundleAssembler>;
fn add_spend(
self: &mut BundleAssembler,
cv: &[u8; 32],
anchor: &[u8; 32],
nullifier: [u8; 32],
rk: &[u8; 32],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
spend_auth_sig: &[u8; 64],
) -> bool;
fn add_output(
self: &mut BundleAssembler,
cv: &[u8; 32],
cmu: &[u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: [u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn finish_bundle_assembly(
assembler: Box<BundleAssembler>,
value_balance: i64,
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>;
#[allow(clippy::too_many_arguments)]
fn check_spend(
self: &mut Verifier,
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(
self: &mut Verifier,
cv: &[u8; 32],
cm: &[u8; 32],
ephemeral_key: &[u8; 32],
zkproof: &[u8; 192], // GROTH_PROOF_SIZE
) -> bool;
fn final_check(
self: &Verifier,
value_balance: i64,
binding_sig: &[u8; 64],
sighash_value: &[u8; 32],
) -> bool;
type BatchValidator;
fn init_batch_validator(cache_store: bool) -> Box<BatchValidator>;
fn check_bundle(self: &mut BatchValidator, bundle: Box<Bundle>, sighash: [u8; 32]) -> bool;
fn validate(self: &mut BatchValidator) -> bool;
}
}
struct Bundle(sapling::Bundle<sapling::Authorized>);
pub(crate) struct Bundle(sapling::Bundle<sapling::Authorized>);
impl Bundle {
fn commitment<D: TransactionDigest<Authorized>>(&self, digester: D) -> D::SaplingDigest {
@ -143,12 +44,12 @@ impl Bundle {
}
}
struct BundleAssembler {
pub(crate) struct BundleAssembler {
shielded_spends: Vec<sapling::SpendDescription<sapling::Authorized>>,
shielded_outputs: Vec<sapling::OutputDescription<[u8; 192]>>, // GROTH_PROOF_SIZE
}
fn new_bundle_assembler() -> Box<BundleAssembler> {
pub(crate) fn new_bundle_assembler() -> Box<BundleAssembler> {
Box::new(BundleAssembler {
shielded_spends: vec![],
shielded_outputs: vec![],
@ -156,7 +57,7 @@ fn new_bundle_assembler() -> Box<BundleAssembler> {
}
impl BundleAssembler {
fn add_spend(
pub(crate) fn add_spend(
self: &mut BundleAssembler,
cv: &[u8; 32],
anchor: &[u8; 32],
@ -203,7 +104,7 @@ impl BundleAssembler {
true
}
fn add_output(
pub(crate) fn add_output(
self: &mut BundleAssembler,
cv: &[u8; 32],
cm: &[u8; 32],
@ -239,7 +140,7 @@ impl BundleAssembler {
}
#[allow(clippy::boxed_local)]
fn finish_bundle_assembly(
pub(crate) fn finish_bundle_assembly(
assembler: Box<BundleAssembler>,
value_balance: i64,
binding_sig: [u8; 64],
@ -255,15 +156,15 @@ fn finish_bundle_assembly(
)))
}
struct Prover(SaplingProvingContext);
pub(crate) struct Prover(SaplingProvingContext);
fn init_prover() -> Box<Prover> {
pub(crate) fn init_prover() -> Box<Prover> {
Box::new(Prover(SaplingProvingContext::new()))
}
impl Prover {
#[allow(clippy::too_many_arguments)]
fn create_spend_proof(
pub(crate) fn create_spend_proof(
&mut self,
ak: &[u8; 32],
nsk: &[u8; 32],
@ -361,7 +262,8 @@ impl Prover {
true
}
fn create_output_proof(
pub(crate) fn create_output_proof(
&mut self,
esk: &[u8; 32],
payment_address: &[u8; 43],
@ -409,7 +311,8 @@ impl Prover {
true
}
fn binding_sig(
pub(crate) fn binding_sig(
&mut self,
value_balance: i64,
sighash: &[u8; 32],
@ -434,9 +337,9 @@ impl Prover {
}
}
struct Verifier(SaplingVerificationContext);
pub(crate) struct Verifier(SaplingVerificationContext);
fn init_verifier() -> Box<Verifier> {
pub(crate) 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.
@ -445,7 +348,7 @@ fn init_verifier() -> Box<Verifier> {
impl Verifier {
#[allow(clippy::too_many_arguments)]
fn check_spend(
pub(crate) fn check_spend(
&mut self,
cv: &[u8; 32],
anchor: &[u8; 32],
@ -500,7 +403,8 @@ impl Verifier {
),
)
}
fn check_output(
pub(crate) fn check_output(
&mut self,
cv: &[u8; 32],
cm: &[u8; 32],
@ -543,7 +447,8 @@ impl Verifier {
),
)
}
fn final_check(
pub(crate) fn final_check(
&self,
value_balance: i64,
binding_sig: &[u8; 64],
@ -570,9 +475,9 @@ struct BatchValidatorInner {
queued_entries: CacheEntries,
}
struct BatchValidator(Option<BatchValidatorInner>);
pub(crate) struct BatchValidator(Option<BatchValidatorInner>);
fn init_batch_validator(cache_store: bool) -> Box<BatchValidator> {
pub(crate) fn init_batch_validator(cache_store: bool) -> Box<BatchValidator> {
Box::new(BatchValidator(Some(BatchValidatorInner {
validator: sapling_proofs::BatchValidator::new(),
queued_entries: CacheEntries::new(cache_store),
@ -594,7 +499,7 @@ impl BatchValidator {
/// the global bundle validity cache, it will have been removed (and this method will
/// return `true`).
#[allow(clippy::boxed_local)]
fn check_bundle(&mut self, bundle: Box<Bundle>, sighash: [u8; 32]) -> bool {
pub(crate) fn check_bundle(&mut self, bundle: Box<Bundle>, sighash: [u8; 32]) -> bool {
if let Some(inner) = &mut self.0 {
let cache = sapling_bundle_validity_cache();
@ -637,7 +542,7 @@ impl BatchValidator {
/// If this batch was configured to cache the results, then if this method returns
/// `true` every bundle added to the batch will have also been added to the global
/// bundle validity cache.
fn validate(&mut self) -> bool {
pub(crate) fn validate(&mut self) -> bool {
if let Some(inner) = self.0.take() {
if inner.validator.validate(
unsafe { SAPLING_SPEND_VK.as_ref() }

View File

@ -21,95 +21,7 @@ use zcash_primitives::{
},
};
use crate::{
note_encryption::{
parse_and_prepare_sapling_ivk, try_sapling_note_decryption, try_sapling_output_recovery,
DecryptedSaplingOutput,
},
params::{network, Network},
};
#[cxx::bridge]
pub(crate) mod ffi {
#[namespace = "wallet"]
pub(crate) struct SaplingShieldedOutput {
cv: [u8; 32],
cmu: [u8; 32],
ephemeral_key: [u8; 32],
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
}
#[namespace = "wallet"]
struct SaplingDecryptionResult {
txid: [u8; 32],
output: u32,
ivk: [u8; 32],
diversifier: [u8; 11],
pk_d: [u8; 32],
}
#[namespace = "consensus"]
extern "Rust" {
type Network;
fn network(
network: &str,
overwinter: i32,
sapling: i32,
blossom: i32,
heartwood: i32,
canopy: i32,
nu5: i32,
) -> Result<Box<Network>>;
}
#[namespace = "wallet"]
extern "Rust" {
fn try_sapling_note_decryption(
network: &Network,
height: u32,
raw_ivk: &[u8; 32],
output: SaplingShieldedOutput,
) -> Result<Box<DecryptedSaplingOutput>>;
fn try_sapling_output_recovery(
network: &Network,
height: u32,
ovk: [u8; 32],
output: SaplingShieldedOutput,
) -> Result<Box<DecryptedSaplingOutput>>;
type DecryptedSaplingOutput;
fn note_value(self: &DecryptedSaplingOutput) -> u64;
fn note_rseed(self: &DecryptedSaplingOutput) -> [u8; 32];
fn zip_212_enabled(self: &DecryptedSaplingOutput) -> bool;
fn recipient_d(self: &DecryptedSaplingOutput) -> [u8; 11];
fn recipient_pk_d(self: &DecryptedSaplingOutput) -> [u8; 32];
fn memo(self: &DecryptedSaplingOutput) -> [u8; 512];
type BatchScanner;
type BatchResult;
fn init_batch_scanner(
network: &Network,
sapling_ivks: &[[u8; 32]],
) -> Result<Box<BatchScanner>>;
fn add_transaction(
self: &mut BatchScanner,
block_tag: [u8; 32],
tx_bytes: &[u8],
height: u32,
) -> Result<()>;
fn flush(self: &mut BatchScanner);
fn collect_results(
self: &mut BatchScanner,
block_tag: [u8; 32],
txid: [u8; 32],
) -> Box<BatchResult>;
fn get_sapling(self: &BatchResult) -> Vec<SaplingDecryptionResult>;
}
}
use crate::{bridge::ffi, note_encryption::parse_and_prepare_sapling_ivk, params::Network};
/// The minimum number of outputs to trial decrypt in a batch.
///
@ -641,7 +553,7 @@ type SaplingRunner =
BatchRunner<[u8; 32], SaplingDomain<Network>, OutputDescription<GrothProofBytes>, WithUsage>;
/// A batch scanner for the `zcashd` wallet.
struct BatchScanner {
pub(crate) struct BatchScanner {
params: Network,
sapling_runner: Option<SaplingRunner>,
}
@ -656,7 +568,7 @@ impl DynamicUsage for BatchScanner {
}
}
fn init_batch_scanner(
pub(crate) fn init_batch_scanner(
network: &Network,
sapling_ivks: &[[u8; 32]],
) -> Result<Box<BatchScanner>, &'static str> {
@ -690,7 +602,7 @@ impl BatchScanner {
/// After adding the outputs, any accumulated batch of sufficient size is run on the
/// global threadpool. Subsequent calls to `Self::add_transaction` will accumulate
/// those output kinds into new batches.
fn add_transaction(
pub(crate) fn add_transaction(
&mut self,
block_tag: [u8; 32],
tx_bytes: &[u8],
@ -724,7 +636,7 @@ impl BatchScanner {
/// Runs the currently accumulated batches on the global threadpool.
///
/// Subsequent calls to `Self::add_transaction` will be accumulated into new batches.
fn flush(&mut self) {
pub(crate) fn flush(&mut self) {
if let Some(runner) = &mut self.sapling_runner {
runner.flush();
}
@ -737,7 +649,11 @@ impl BatchScanner {
/// mempool change).
///
/// TODO: Return the `HashMap`s directly once `cxx` supports it.
fn collect_results(&mut self, block_tag: [u8; 32], txid: [u8; 32]) -> Box<BatchResult> {
pub(crate) fn collect_results(
&mut self,
block_tag: [u8; 32],
txid: [u8; 32],
) -> Box<BatchResult> {
let block_tag = BlockHash(block_tag);
let txid = TxId::from_bytes(txid);
@ -754,12 +670,12 @@ impl BatchScanner {
}
}
struct BatchResult {
pub(crate) struct BatchResult {
sapling: HashMap<(TxId, usize), DecryptedNote<[u8; 32], SaplingDomain<Network>>>,
}
impl BatchResult {
fn get_sapling(&self) -> Vec<ffi::SaplingDecryptionResult> {
pub(crate) fn get_sapling(&self) -> Vec<ffi::SaplingDecryptionResult> {
self.sapling
.iter()
.map(

View File

@ -34,7 +34,7 @@
#include "librustzcash.h"
#include <rust/bundlecache.h>
#include <rust/bridge.h>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;

View File

@ -35,8 +35,8 @@
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <rust/bridge.h>
#include <rust/ed25519.h>
#include <rust/sapling.h>
#include <rust/orchard.h>
#include <univalue.h>

View File

@ -22,8 +22,8 @@
#include <optional>
#include <rust/bridge.h>
#include <rust/builder.h>
#include <rust/sapling.h>
#define NO_MEMO {{0xF6}}

View File

@ -42,7 +42,7 @@
#include <utility>
#include <vector>
#include <rust/wallet_scanner.h>
#include <rust/bridge.h>
#include <boost/shared_ptr.hpp>

View File

@ -12,7 +12,7 @@
#include "zcash/util.h"
#include <primitives/orchard.h>
#include <rust/merkle_frontier.h>
#include <rust/bridge.h>
namespace libzcash {
@ -316,8 +316,7 @@ public:
}
bool AppendBundle(const OrchardBundle& bundle) {
return inner->append_bundle(
reinterpret_cast<merkle_frontier::OrchardBundle*>(bundle.inner.get()));
return inner->append_bundle(*bundle.GetDetails());
}
const uint256 root() const {

View File

@ -11,7 +11,7 @@
#include <array>
#include <optional>
#include <rust/wallet_scanner.h>
#include <rust/bridge.h>
namespace libzcash {

View File

@ -35,8 +35,8 @@
#include "zcash/Note.hpp"
#include "librustzcash.h"
#include <rust/bridge.h>
#include <rust/ed25519/types.h>
#include <rust/sapling.h>
using namespace libzcash;
// This method is based on Shutdown from init.cpp