Merge pull request #6533 from str4d/6397-cxx-rust-streams

Expand `CppStream` to cover all `Stream`-like C++ types
This commit is contained in:
str4d 2023-04-07 09:42:08 +01:00 committed by GitHub
commit 3ef6e96fa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 193 additions and 30 deletions

View File

@ -284,6 +284,7 @@ BITCOIN_CORE_H = \
serialize.h \
spentindex.h \
streams.h \
streams_rust.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
@ -527,6 +528,7 @@ libbitcoin_common_a_SOURCES = \
script/script_error.cpp \
script/sign.cpp \
script/standard.cpp \
streams_rust.cpp \
transaction_builder.cpp \
util/test.cpp \
warnings.cpp \

View File

@ -132,6 +132,11 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
void write_u8(const unsigned char* pch, size_t nSize)
{
ctx.Write(pch, nSize);
}
void write(const char *pch, size_t size) {
ctx.Write((const unsigned char*)pch, size);
}

View File

@ -19,16 +19,35 @@ use crate::{
new_bundle_assembler, BatchValidator, Bundle as SaplingBundle,
BundleAssembler as SaplingBundleAssembler, Prover, Verifier,
},
streams::{
from_auto_file, from_buffered_file, from_data, from_hash_writer, from_size_computer,
CppStream,
},
wallet_scanner::{init_batch_scanner, BatchResult, BatchScanner},
};
#[cxx::bridge]
pub(crate) mod ffi {
extern "C++" {
include!("hash.h");
include!("streams.h");
#[cxx_name = "RustDataStream"]
type RustStream = crate::streams::ffi::RustStream;
type CAutoFile = crate::streams::ffi::CAutoFile;
type CBufferedFile = crate::streams::ffi::CBufferedFile;
type CHashWriter = crate::streams::ffi::CHashWriter;
type CSizeComputer = crate::streams::ffi::CSizeComputer;
}
#[namespace = "stream"]
extern "Rust" {
type CppStream<'a>;
fn from_data(stream: Pin<&mut RustStream>) -> Box<CppStream<'_>>;
fn from_auto_file(file: Pin<&mut CAutoFile>) -> Box<CppStream<'_>>;
fn from_buffered_file(file: Pin<&mut CBufferedFile>) -> Box<CppStream<'_>>;
fn from_hash_writer(writer: Pin<&mut CHashWriter>) -> Box<CppStream<'_>>;
fn from_size_computer(sc: Pin<&mut CSizeComputer>) -> Box<CppStream<'_>>;
}
#[namespace = "consensus"]
@ -201,9 +220,9 @@ pub(crate) mod ffi {
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 parse_orchard(reader: &mut CppStream<'_>) -> Result<Box<Orchard>>;
fn serialize(self: &Orchard, writer: &mut CppStream<'_>) -> Result<()>;
fn serialize_legacy(self: &Orchard, writer: &mut CppStream<'_>) -> Result<()>;
fn dynamic_memory_usage(self: &Orchard) -> usize;
fn root(self: &Orchard) -> [u8; 32];
fn size(self: &Orchard) -> u64;

View File

@ -1,5 +1,4 @@
use core::mem::size_of_val;
use core::pin::Pin;
use incrementalmerkletree::{bridgetree, Altitude, Frontier, Hashable};
use orchard::tree::MerkleHashOrchard;
@ -9,7 +8,7 @@ use zcash_primitives::merkle_tree::{
CommitmentTree, HashSer,
};
use crate::{bridge::ffi, orchard_bundle, streams::CppStream, wallet::Wallet};
use crate::{orchard_bundle, streams::CppStream, wallet::Wallet};
pub const MERKLE_DEPTH: u8 = 32;
@ -26,9 +25,7 @@ impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
}
/// Attempts to parse a Merkle frontier from the given C++ stream.
pub(crate) fn parse(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Self>, String> {
let reader = CppStream::from(stream);
pub(crate) fn parse(reader: &mut CppStream<'_>) -> Result<Box<Self>, String> {
match read_frontier_v1(reader) {
Ok(parsed) => Ok(Box::new(MerkleFrontier(parsed))),
Err(e) => Err(format!("Failed to parse v5 Merkle frontier: {}", e)),
@ -36,15 +33,13 @@ impl<H: Copy + Hashable + HashSer> MerkleFrontier<H> {
}
/// Serializes the frontier to the given C++ stream.
pub(crate) fn serialize(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
let writer = CppStream::from(stream);
pub(crate) fn serialize(&self, writer: &mut CppStream<'_>) -> Result<(), String> {
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.
pub(crate) fn serialize_legacy(&self, stream: Pin<&mut ffi::RustStream>) -> Result<(), String> {
let writer = CppStream::from(stream);
pub(crate) fn serialize_legacy(&self, writer: &mut CppStream<'_>) -> Result<(), String> {
let commitment_tree = CommitmentTree::from_frontier(&self.0);
commitment_tree.write(writer).map_err(|e| {
format!(
@ -93,8 +88,8 @@ pub(crate) fn new_orchard() -> Box<Orchard> {
}
/// Attempts to parse an Orchard Merkle frontier from the given C++ stream.
pub(crate) fn parse_orchard(stream: Pin<&mut ffi::RustStream>) -> Result<Box<Orchard>, String> {
Orchard::parse(stream)
pub(crate) fn parse_orchard(reader: &mut CppStream<'_>) -> Result<Box<Orchard>, String> {
Orchard::parse(reader)
}
pub(crate) struct OrchardWallet;

View File

@ -4,41 +4,116 @@ use std::pin::Pin;
#[cxx::bridge]
pub(crate) mod ffi {
extern "C++" {
include!("hash.h");
include!("streams.h");
#[cxx_name = "RustDataStream"]
type RustStream;
unsafe fn read_u8(self: Pin<&mut RustStream>, pch: *mut u8, nSize: usize) -> Result<()>;
unsafe fn write_u8(self: Pin<&mut RustStream>, pch: *const u8, nSize: usize) -> Result<()>;
type CAutoFile;
unsafe fn read_u8(self: Pin<&mut CAutoFile>, pch: *mut u8, nSize: usize) -> Result<()>;
unsafe fn write_u8(self: Pin<&mut CAutoFile>, pch: *const u8, nSize: usize) -> Result<()>;
type CBufferedFile;
unsafe fn read_u8(self: Pin<&mut CBufferedFile>, pch: *mut u8, nSize: usize) -> Result<()>;
type CHashWriter;
unsafe fn write_u8(self: Pin<&mut CHashWriter>, pch: *const u8, nSize: usize)
-> Result<()>;
type CSizeComputer;
unsafe fn write_u8(
self: Pin<&mut CSizeComputer>,
pch: *const u8,
nSize: usize,
) -> Result<()>;
}
impl UniquePtr<RustStream> {}
impl UniquePtr<CAutoFile> {}
impl UniquePtr<CBufferedFile> {}
impl UniquePtr<CHashWriter> {}
impl UniquePtr<CSizeComputer> {}
}
pub struct CppStream<'a> {
inner: Pin<&'a mut ffi::RustStream>,
pub(crate) fn from_data(stream: Pin<&mut ffi::RustStream>) -> Box<CppStream<'_>> {
Box::new(CppStream::Data(stream))
}
impl<'a> From<Pin<&'a mut ffi::RustStream>> for CppStream<'a> {
fn from(inner: Pin<&'a mut ffi::RustStream>) -> Self {
Self { inner }
}
pub(crate) fn from_auto_file(file: Pin<&mut ffi::CAutoFile>) -> Box<CppStream<'_>> {
Box::new(CppStream::AutoFile(file))
}
pub(crate) fn from_buffered_file(file: Pin<&mut ffi::CBufferedFile>) -> Box<CppStream<'_>> {
Box::new(CppStream::BufferedFile(file))
}
pub(crate) fn from_hash_writer(writer: Pin<&mut ffi::CHashWriter>) -> Box<CppStream<'_>> {
Box::new(CppStream::Hash(writer))
}
pub(crate) fn from_size_computer(sc: Pin<&mut ffi::CSizeComputer>) -> Box<CppStream<'_>> {
Box::new(CppStream::Size(sc))
}
pub(crate) enum CppStream<'a> {
Data(Pin<&'a mut ffi::RustStream>),
AutoFile(Pin<&'a mut ffi::CAutoFile>),
BufferedFile(Pin<&'a mut ffi::CBufferedFile>),
Hash(Pin<&'a mut ffi::CHashWriter>),
Size(Pin<&'a mut ffi::CSizeComputer>),
}
impl<'a> io::Read for CppStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe { self.inner.as_mut().read_u8(buf.as_mut_ptr(), buf.len()) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
let pch = buf.as_mut_ptr();
let len = buf.len();
match self {
CppStream::Data(inner) => unsafe { inner.as_mut().read_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::AutoFile(inner) => unsafe { inner.as_mut().read_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::BufferedFile(inner) => unsafe { inner.as_mut().read_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::Hash(_) => Err(io::Error::new(
io::ErrorKind::Unsupported,
"Cannot read from CHashWriter",
)),
CppStream::Size(_) => Err(io::Error::new(
io::ErrorKind::Unsupported,
"Cannot read from CSizeComputer",
)),
}
}
}
impl<'a> io::Write for CppStream<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { self.inner.as_mut().write_u8(buf.as_ptr(), buf.len()) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
let pch = buf.as_ptr();
let len = buf.len();
match self {
CppStream::Data(inner) => unsafe { inner.as_mut().write_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::AutoFile(inner) => unsafe { inner.as_mut().write_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::BufferedFile(_) => Err(io::Error::new(
io::ErrorKind::Unsupported,
"Cannot write to CBufferedFile",
)),
CppStream::Hash(inner) => unsafe { inner.as_mut().write_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
CppStream::Size(inner) => unsafe { inner.as_mut().write_u8(pch, len) }
.map(|()| buf.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
}
}
fn flush(&mut self) -> io::Result<()> {

View File

@ -1079,6 +1079,11 @@ protected:
public:
CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
void write_u8(const unsigned char* pch, size_t nSize)
{
write(reinterpret_cast<const char*>(pch), nSize);
}
void write(const char *psz, size_t _nSize)
{
this->nSize += _nSize;

View File

@ -509,6 +509,11 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
void read_u8(unsigned char* pch, size_t nSize)
{
read(reinterpret_cast<char*>(pch), nSize);
}
void read(char* pch, size_t nSize)
{
if (!file)
@ -530,6 +535,11 @@ public:
}
}
void write_u8(const unsigned char* pch, size_t nSize)
{
write(reinterpret_cast<const char*>(pch), nSize);
}
void write(const char* pch, size_t nSize)
{
if (!file)
@ -631,6 +641,11 @@ public:
return src == NULL || (nReadPos == nSrcPos && feof(src));
}
void read_u8(unsigned char* pch, size_t nSize)
{
read(reinterpret_cast<char*>(pch), nSize);
}
// read a number of bytes
void read(char *pch, size_t nSize) {
if (nSize == 0) return;

25
src/streams_rust.cpp Normal file
View File

@ -0,0 +1,25 @@
// Copyright (c) 2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "streams_rust.h"
rust::Box<stream::CppStream> ToRustStream(RustDataStream& stream) {
return stream::from_data(stream);
}
rust::Box<stream::CppStream> ToRustStream(CAutoFile& file) {
return stream::from_auto_file(file);
}
rust::Box<stream::CppStream> ToRustStream(CBufferedFile& file) {
return stream::from_buffered_file(file);
}
rust::Box<stream::CppStream> ToRustStream(CHashWriter& writer) {
return stream::from_hash_writer(writer);
}
rust::Box<stream::CppStream> ToRustStream(CSizeComputer& sc) {
return stream::from_size_computer(sc);
}

21
src/streams_rust.h Normal file
View File

@ -0,0 +1,21 @@
// Copyright (c) 2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_STREAMS_RUST_H
#define ZCASH_STREAMS_RUST_H
#include "hash.h"
#include "serialize.h"
#include "streams.h"
#include <rust/bridge.h>
#include <rust/cxx.h>
rust::Box<stream::CppStream> ToRustStream(RustDataStream& stream);
rust::Box<stream::CppStream> ToRustStream(CAutoFile& file);
rust::Box<stream::CppStream> ToRustStream(CBufferedFile& file);
rust::Box<stream::CppStream> ToRustStream(CHashWriter& writer);
rust::Box<stream::CppStream> ToRustStream(CSizeComputer& sc);
#endif // ZCASH_STREAMS_RUST_H

View File

@ -7,6 +7,7 @@
#include "uint256.h"
#include "serialize.h"
#include "streams_rust.h"
#include "Zcash.h"
#include "zcash/util.h"
@ -296,7 +297,7 @@ public:
template<typename Stream>
void Serialize(Stream& s) const {
try {
inner->serialize(s);
inner->serialize(*ToRustStream(s));
} catch (const std::exception& e) {
throw std::ios_base::failure(e.what());
}
@ -305,7 +306,7 @@ public:
template<typename Stream>
void Unserialize(Stream& s) {
try {
inner = merkle_frontier::parse_orchard(s);
inner = merkle_frontier::parse_orchard(*ToRustStream(s));
} catch (const std::exception& e) {
throw std::ios_base::failure(e.what());
}
@ -341,7 +342,7 @@ public:
template<typename Stream>
void Serialize(Stream& s) const {
try {
frontier.inner->serialize_legacy(s);
frontier.inner->serialize_legacy(*ToRustStream(s));
} catch (const std::exception& e) {
throw std::ios_base::failure(e.what());
}