Migrate Equihash Rust FFI to `cxx`
This integrates `cxxbridge` into the build system, adding its generated source files to `libzcash`. We currently need to manually specify each Rust file containing a bridge description.
This commit is contained in:
parent
21f1bbf4aa
commit
54aeb2c408
|
@ -83,6 +83,7 @@ Makefile
|
|||
# Rust
|
||||
.cargo/.configured-for-*
|
||||
.cargo/config
|
||||
src/rust/gen/
|
||||
target/
|
||||
|
||||
# Unit-tests
|
||||
|
|
|
@ -15,7 +15,9 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
|
|||
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
|
||||
|
||||
BITCOIN_CONFIG_INCLUDES += -I$(srcdir)/rust/include
|
||||
BITCOIN_CONFIG_INCLUDES += -I$(srcdir)/rust/gen/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/rust/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/rust/gen/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/univalue/include
|
||||
|
||||
|
@ -42,6 +44,20 @@ if ENABLE_WALLET
|
|||
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
||||
endif
|
||||
|
||||
# TODO: Figure out how to avoid an explicit file list.
|
||||
CXXBRIDGE_RS = rust/src/equihash.rs
|
||||
CXXBRIDGE_H = rust/gen/include/rust/equihash.h
|
||||
CXXBRIDGE_CPP = rust/gen/src/equihash.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.
|
||||
CXXBRIDGE_OPTS = -i rust/cxx.h
|
||||
|
||||
$(CXXBRIDGE_RS): ;
|
||||
$(CXXBRIDGE_H) $(CXXBRIDGE_CPP): $(CXXBRIDGE_RS)
|
||||
@$(MKDIR_P) $(@D)
|
||||
$(AM_V_GEN)$(CXXBRIDGE) $(CXXBRIDGE_OPTS) $< -o $@
|
||||
|
||||
# We pass through CC etc. flags so they are available to Rust dependencies that internally
|
||||
# compile C or C++ code with the `cc` crate.
|
||||
#
|
||||
|
@ -572,6 +588,7 @@ zcash_tx_LDADD += $(BOOST_LIBS)
|
|||
|
||||
# zcash protocol primitives #
|
||||
libzcash_a_SOURCES = \
|
||||
$(CXXBRIDGE_CPP) \
|
||||
zcash/IncrementalMerkleTree.cpp \
|
||||
zcash/NoteEncryption.cpp \
|
||||
zcash/Address.cpp \
|
||||
|
@ -617,7 +634,7 @@ endif
|
|||
|
||||
libzcash_script_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
||||
libzcash_script_la_LIBADD = $(LIBSECP256K1)
|
||||
libzcash_script_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/rust/include -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
|
||||
libzcash_script_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/rust/include -I$(srcdir)/rust/gen/include -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
|
||||
libzcash_script_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
endif
|
||||
|
@ -629,6 +646,8 @@ CTAES_DIST += crypto/ctaes/ctaes.h
|
|||
CTAES_DIST += crypto/ctaes/README.md
|
||||
CTAES_DIST += crypto/ctaes/test.c
|
||||
|
||||
BUILT_SOURCES = $(CXXBRIDGE_H)
|
||||
|
||||
CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno $(bin_SCRIPTS)
|
||||
|
||||
DISTCLEANFILES = obj/build.h
|
||||
|
@ -640,6 +659,7 @@ clean-local:
|
|||
-$(MAKE) -C secp256k1 clean
|
||||
-$(MAKE) -C univalue clean
|
||||
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
|
||||
rm -f rust/gen
|
||||
rm -f fuzz.cpp
|
||||
rm -rf fuzzing/*/output
|
||||
-rm -f config.h
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "uint256.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
#include <rust/equihash.h>
|
||||
|
||||
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
|
||||
{
|
||||
|
@ -114,11 +115,11 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params&
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << I;
|
||||
|
||||
return librustzcash_eh_isvalid(
|
||||
return equihash::is_valid(
|
||||
n, k,
|
||||
(unsigned char*)&ss[0], ss.size(),
|
||||
pblock->nNonce.begin(), pblock->nNonce.size(),
|
||||
pblock->nSolution.data(), pblock->nSolution.size());
|
||||
{(const unsigned char*)ss.data(), ss.size()},
|
||||
{pblock->nNonce.begin(), pblock->nNonce.size()},
|
||||
{pblock->nSolution.data(), pblock->nSolution.size()});
|
||||
}
|
||||
|
||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
|
||||
|
|
|
@ -38,19 +38,6 @@ extern "C" {
|
|||
size_t sprout_path_len
|
||||
);
|
||||
|
||||
/// Validates the provided Equihash solution against
|
||||
/// the given parameters, input and nonce.
|
||||
bool librustzcash_eh_isvalid(
|
||||
uint32_t n,
|
||||
uint32_t k,
|
||||
const unsigned char* input,
|
||||
size_t input_len,
|
||||
const unsigned char* nonce,
|
||||
size_t nonce_len,
|
||||
const unsigned char* soln,
|
||||
size_t soln_len
|
||||
);
|
||||
|
||||
/// Writes the "uncommitted" note value for empty leaves
|
||||
/// of the merkle tree. `result` must be a valid pointer
|
||||
/// to 32 bytes which will be written.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
use tracing::error;
|
||||
use zcash_primitives::block::equihash;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
#[namespace = "equihash"]
|
||||
extern "Rust" {
|
||||
fn is_valid(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the provided Equihash solution against the given parameters, input
|
||||
/// and nonce.
|
||||
fn is_valid(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool {
|
||||
let expected_soln_len = (1 << k) * ((n / (k + 1)) as usize + 1) / 8;
|
||||
if (k >= n) || (n % 8 != 0) || (soln.len() != expected_soln_len) {
|
||||
error!(
|
||||
"equihash::is_valid: params wrong, n={}, k={}, soln_len={} expected={}",
|
||||
n,
|
||||
k,
|
||||
soln.len(),
|
||||
expected_soln_len,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if let Err(e) = equihash::is_valid_solution(n, k, input, nonce, soln) {
|
||||
error!("equihash::is_valid: is_valid_solution: {}", e);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::slice;
|
||||
use std::sync::Once;
|
||||
use subtle::CtOption;
|
||||
use tracing::{error, info};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::ffi::OsStr;
|
||||
|
@ -44,7 +44,6 @@ use std::ffi::OsString;
|
|||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use zcash_primitives::{
|
||||
block::equihash,
|
||||
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
|
||||
merkle_tree::MerklePath,
|
||||
sapling::{
|
||||
|
@ -67,6 +66,7 @@ use zcash_proofs::{
|
|||
|
||||
mod blake2b;
|
||||
mod ed25519;
|
||||
mod equihash;
|
||||
mod metrics_ffi;
|
||||
mod streams_ffi;
|
||||
mod tracing_ffi;
|
||||
|
@ -528,38 +528,6 @@ pub extern "C" fn librustzcash_sapling_ka_derivepublic(
|
|||
true
|
||||
}
|
||||
|
||||
/// Validates the provided Equihash solution against the given parameters, input
|
||||
/// and nonce.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_eh_isvalid(
|
||||
n: u32,
|
||||
k: u32,
|
||||
input: *const c_uchar,
|
||||
input_len: size_t,
|
||||
nonce: *const c_uchar,
|
||||
nonce_len: size_t,
|
||||
soln: *const c_uchar,
|
||||
soln_len: size_t,
|
||||
) -> bool {
|
||||
let expected_soln_len = (1 << k) * ((n / (k + 1)) as usize + 1) / 8;
|
||||
if (k >= n) || (n % 8 != 0) || (soln_len != expected_soln_len) {
|
||||
error!(
|
||||
"eh_isvalid: params wrong, n={}, k={}, soln_len={} expected={}",
|
||||
n, k, soln_len, expected_soln_len,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
let rs_input = unsafe { slice::from_raw_parts(input, input_len) };
|
||||
let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) };
|
||||
let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) };
|
||||
if let Err(e) = equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) {
|
||||
error!("eh_isvalid: is_valid_solution: {}", e);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Sapling verification context. Please free this when you're done.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn librustzcash_sapling_verification_ctx_init(
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "test/test_bitcoin.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include "librustzcash.h"
|
||||
#include <rust/equihash.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
|
@ -95,11 +95,11 @@ void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I,
|
|||
PrintSolution(strm, soln);
|
||||
BOOST_TEST_MESSAGE(strm.str());
|
||||
|
||||
bool isValid = librustzcash_eh_isvalid(
|
||||
bool isValid = equihash::is_valid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
minimal.data(), minimal.size());
|
||||
{(const unsigned char*)I.data(), I.size()},
|
||||
{V.begin(), V.size()},
|
||||
{minimal.data(), minimal.size()});
|
||||
BOOST_CHECK(isValid == expected);
|
||||
}
|
||||
|
||||
|
@ -213,22 +213,25 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
|
|||
size_t cBitLen { n/(k+1) };
|
||||
std::vector<unsigned char> sol_char = GetMinimalFromIndices(soln, cBitLen);
|
||||
|
||||
rust::Slice<const uint8_t> input{(unsigned char*)&I[0], I.size()};
|
||||
rust::Slice<const uint8_t> nonce{V.begin(), V.size()};
|
||||
|
||||
// Prove that the solution is valid.
|
||||
BOOST_CHECK(librustzcash_eh_isvalid(
|
||||
BOOST_CHECK(equihash::is_valid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
sol_char.data(), sol_char.size()));
|
||||
input,
|
||||
nonce,
|
||||
{sol_char.data(), sol_char.size()}));
|
||||
|
||||
// Changing any single bit of the encoded solution should make it invalid.
|
||||
for (size_t i = 0; i < sol_char.size() * 8; i++) {
|
||||
std::vector<unsigned char> mutated = sol_char;
|
||||
mutated.at(i/8) ^= (1 << (i % 8));
|
||||
BOOST_CHECK(!librustzcash_eh_isvalid(
|
||||
BOOST_CHECK(!equihash::is_valid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
mutated.data(), mutated.size()));
|
||||
input,
|
||||
nonce,
|
||||
{mutated.data(), mutated.size()}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -246,11 +246,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
}
|
||||
|
||||
for (auto soln : solns) {
|
||||
if (!librustzcash_eh_isvalid(
|
||||
if (!equihash::is_valid(
|
||||
n, k,
|
||||
(unsigned char*)&ss[0], ss.size(),
|
||||
pblock->nNonce.begin(), pblock->nNonce.size(),
|
||||
soln.data(), soln.size())) continue;
|
||||
{(const unsigned char*)ss.data(), ss.size()},
|
||||
{pblock->nNonce.begin(), pblock->nNonce.size()},
|
||||
{soln.data(), soln.size()})) continue;
|
||||
pblock->nSolution = soln;
|
||||
|
||||
CValidationState state;
|
||||
|
|
|
@ -31,6 +31,7 @@ rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/
|
|||
rm -rf cache
|
||||
rm -rf target
|
||||
rm -rf depends/work
|
||||
rm -rf src/rust/gen
|
||||
|
||||
find src -type f -and \( -name '*.Po' -or -name '*.Plo' -or -name '*.o' -or -name '*.a' -or -name '*.lib' -or -name '*.la' -or -name '*.lo' -or -name '*.lai' -or -name '*.pc' -or -name '.dirstamp' -or -name '*.gcda' -or -name '*.gcno' -or -name '*.sage.py' -or -name '*.trs' \) -delete
|
||||
|
||||
|
|
Loading…
Reference in New Issue