Merge pull request #5357 from nuttycom/feature/wallet_orchard-add_key_types

Add Orchard Address, IncomingViewingKey, and FullViewingKey types.
This commit is contained in:
Kris Nuttycombe 2021-11-30 07:13:22 -07:00 committed by GitHub
commit aa808eb07d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 875 additions and 30 deletions

23
Cargo.lock generated
View File

@ -521,7 +521,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "equihash"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"blake2b_simd",
"byteorder",
@ -530,7 +530,7 @@ dependencies = [
[[package]]
name = "f4jumble"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"blake2b_simd",
]
@ -1112,7 +1112,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "orchard"
version = "0.0.0"
source = "git+https://github.com/zcash/orchard.git?rev=2c8241f25b943aa05203eacf9905db117c69bd29#2c8241f25b943aa05203eacf9905db117c69bd29"
source = "git+https://github.com/zcash/orchard.git?rev=68b790c7dadade049f44ad4aafa0ff71a3a10e91#68b790c7dadade049f44ad4aafa0ff71a3a10e91"
dependencies = [
"aes",
"arrayvec 0.7.2",
@ -1404,8 +1404,9 @@ dependencies = [
[[package]]
name = "reddsa"
version = "0.0.0"
source = "git+https://github.com/str4d/redjubjub.git?rev=416a6a8ebf8bd42c114c938883016c04f338de72#416a6a8ebf8bd42c114c938883016c04f338de72"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6"
dependencies = [
"blake2b_simd",
"byteorder",
@ -1884,7 +1885,7 @@ dependencies = [
[[package]]
name = "zcash_address"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"bech32",
"blake2b_simd",
@ -1896,7 +1897,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"byteorder",
"nonempty",
@ -1905,7 +1906,7 @@ dependencies = [
[[package]]
name = "zcash_history"
version = "0.2.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"bigint",
"blake2b_simd",
@ -1915,7 +1916,7 @@ dependencies = [
[[package]]
name = "zcash_note_encryption"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"blake2b_simd",
"byteorder",
@ -1930,7 +1931,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"aes",
"bip0039",
@ -1964,7 +1965,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12#3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
dependencies = [
"bellman",
"blake2b_simd",

View File

@ -72,9 +72,9 @@ codegen-units = 1
[patch.crates-io]
ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "3d7b9dc9aa14ecc8f95ed338b2d7876fac12fd12" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "68b790c7dadade049f44ad4aafa0ff71a3a10e91" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }

View File

@ -109,6 +109,7 @@ LIBZCASH_H = \
zcash/Address.hpp \
zcash/address/sapling.hpp \
zcash/address/sprout.hpp \
zcash/address/orchard.h \
zcash/address/zip32.h \
zcash/History.hpp \
zcash/JoinSplit.hpp \
@ -537,6 +538,7 @@ libzcash_a_SOURCES = \
zcash/Address.cpp \
zcash/address/sapling.cpp \
zcash/address/sprout.cpp \
zcash/address/orchard.cpp \
zcash/address/zip32.cpp \
zcash/History.cpp \
zcash/JoinSplit.cpp \

View File

@ -21,7 +21,8 @@ zcash_gtest_SOURCES = \
# depend on global state (see #1539)
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_wallet_zkeys.cpp
wallet/gtest/test_wallet_zkeys.cpp \
wallet/gtest/test_orchard_zkeys.cpp
endif
zcash_gtest_SOURCES += \
test/data/merkle_roots_orchard.h \

View File

@ -0,0 +1,203 @@
// Copyright (c) 2021 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_RUST_INCLUDE_RUST_ORCHARD_KEYS_H
#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_KEYS_H
#include "rust/streams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* A pointer to an Orchard shielded payment address, as defined in
* https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding
*/
struct OrchardRawAddressPtr;
typedef struct OrchardRawAddressPtr OrchardRawAddressPtr;
/**
* Clones the given Orchard payment address and returns
* a pointer to the newly created value. Both the original
* one's memory and the newly allocated one need to be freed
* independently.
*/
OrchardRawAddressPtr* orchard_address_clone(
const OrchardRawAddressPtr* ptr);
/**
* Frees the memory allocated for the given Orchard payment address
*/
void orchard_address_free(OrchardRawAddressPtr* ptr);
//
// Incoming Viewing Keys
//
struct OrchardIncomingViewingKeyPtr;
typedef struct OrchardIncomingViewingKeyPtr OrchardIncomingViewingKeyPtr;
/**
* Clones the given Orchard incoming viewing key and returns
* a pointer to the newly created value. Both the original
* one's memory and the newly allocated one need to be freed
* independently.
*/
OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_clone(
const OrchardIncomingViewingKeyPtr* ptr);
/**
* Frees the memory allocated for the given Orchard incoming viewing key
*/
void orchard_incoming_viewing_key_free(OrchardIncomingViewingKeyPtr* ptr);
/**
* Returns the address at the specified diversifier index.
*/
OrchardRawAddressPtr* orchard_incoming_viewing_key_to_address(
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
const unsigned char* j);
/**
* Parses an Orchard incoming viewing key from the given stream.
*
* - If the key does not parse correctly, the returned pointer will be null.
*/
OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes an Orchard incoming viewing key to the given stream.
*/
bool orchard_incoming_viewing_key_serialize(
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
void* stream,
write_callback_t write_cb);
/**
* Implements the "less than" operation for comparing two keys.
*/
bool orchard_incoming_viewing_key_lt(
const OrchardIncomingViewingKeyPtr* k0,
const OrchardIncomingViewingKeyPtr* k1);
/**
* Implements equality testing between incoming viewing keys.
*/
bool orchard_incoming_viewing_key_eq(
const OrchardIncomingViewingKeyPtr* k0,
const OrchardIncomingViewingKeyPtr* k1);
//
// Full Viewing Keys
//
struct OrchardFullViewingKeyPtr;
typedef struct OrchardFullViewingKeyPtr OrchardFullViewingKeyPtr;
/**
* Clones the given Orchard full viewing key and returns
* a pointer to the newly created value. Both the original
* one's memory and the newly allocated one need to be freed
* independently.
*/
OrchardFullViewingKeyPtr* orchard_full_viewing_key_clone(
const OrchardFullViewingKeyPtr* ptr);
/**
* Free the memory allocated for the given Orchard full viewing key
*/
void orchard_full_viewing_key_free(OrchardFullViewingKeyPtr* ptr);
/**
* Parses an Orchard full viewing key from the given stream.
*
* - If the key does not parse correctly, the returned pointer will be null.
*/
OrchardFullViewingKeyPtr* orchard_full_viewing_key_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes an Orchard full viewing key to the given stream.
*/
bool orchard_full_viewing_key_serialize(
const OrchardFullViewingKeyPtr* full_viewing_key,
void* stream,
write_callback_t write_cb);
/**
* Returns the incoming viewing key for the specified full viewing key.
*/
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key(
const OrchardFullViewingKeyPtr* key);
/**
* Implements equality testing between full viewing keys.
*/
bool orchard_full_viewing_key_eq(
const OrchardFullViewingKeyPtr* k0,
const OrchardFullViewingKeyPtr* k1);
//
// Spending Keys
//
struct OrchardSpendingKeyPtr;
typedef struct OrchardSpendingKeyPtr OrchardSpendingKeyPtr;
/**
* Constructs a spending key by ZIP-32 derivation to the account
* level from the provided seed.
*/
OrchardSpendingKeyPtr* orchard_spending_key_for_account(
const unsigned char* seed,
size_t seed_len,
uint32_t bip44_coin_type,
uint32_t account_id);
/**
* Clones the given Orchard spending key and returns
* a pointer to the newly created value. Both the original
* one's memory and the newly allocated one need to be freed
* independently.
*/
OrchardSpendingKeyPtr* orchard_spending_key_clone(
const OrchardSpendingKeyPtr* ptr);
/**
* Free the memory allocated for the given Orchard spending key
*/
void orchard_spending_key_free(OrchardSpendingKeyPtr* ptr);
/**
* Parses an Orchard spending key from the given stream.
*
* - If the key does not parse correctly, the returned pointer will be null.
*/
OrchardSpendingKeyPtr* orchard_spending_key_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes an Orchard spending key to the given stream.
*/
bool orchard_spending_key_serialize(
const OrchardSpendingKeyPtr* spending_key,
void* stream,
write_callback_t write_cb);
/**
* Returns the full viewing key for the specified spending key.
*/
OrchardFullViewingKeyPtr* orchard_spending_key_to_full_viewing_key(
const OrchardSpendingKeyPtr* key);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_KEYS_H

View File

@ -0,0 +1,287 @@
use std::io::{Read, Write};
use std::slice;
use tracing::error;
use orchard::keys::{DiversifierIndex, FullViewingKey, IncomingViewingKey, SpendingKey};
use orchard::Address;
use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb};
//
// Addresses
//
#[no_mangle]
pub extern "C" fn orchard_address_clone(addr: *const Address) -> *mut Address {
unsafe { addr.as_ref() }
.map(|addr| Box::into_raw(Box::new(*addr)))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_address_free(addr: *mut Address) {
if !addr.is_null() {
drop(unsafe { Box::from_raw(addr) });
}
}
//
// Incoming viewing keys
//
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_clone(
key: *const IncomingViewingKey,
) -> *mut IncomingViewingKey {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(key.clone())))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_free(key: *mut IncomingViewingKey) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_parse(
stream: Option<StreamObj>,
read_cb: Option<ReadCb>,
) -> *mut IncomingViewingKey {
let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
let mut buf = [0u8; 64];
match reader.read_exact(&mut buf) {
Err(e) => {
error!(
"Stream failure reading bytes of Orchard incoming viewing key: {}",
e
);
std::ptr::null_mut()
}
Ok(()) => {
let read = IncomingViewingKey::from_bytes(&buf);
if read.is_some().into() {
Box::into_raw(Box::new(read.unwrap()))
} else {
error!("Failed to parse Orchard incoming viewing key.");
std::ptr::null_mut()
}
}
}
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_to_address(
key: *const IncomingViewingKey,
diversifier_index: *const [u8; 11],
) -> *mut Address {
let key =
unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null.");
let diversifier_index = DiversifierIndex::from(unsafe { *diversifier_index });
Box::into_raw(Box::new(key.address_at(diversifier_index)))
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_serialize(
key: *const IncomingViewingKey,
stream: Option<StreamObj>,
write_cb: Option<WriteCb>,
) -> bool {
let key =
unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null.");
let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
match writer.write_all(&key.to_bytes()) {
Ok(()) => true,
Err(e) => {
error!("Stream failure writing Orchard incoming viewing key: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_lt(
k0: *const IncomingViewingKey,
k1: *const IncomingViewingKey,
) -> bool {
let k0 = unsafe { k0.as_ref() };
let k1 = unsafe { k1.as_ref() };
k0 < k1
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_eq(
k0: *const IncomingViewingKey,
k1: *const IncomingViewingKey,
) -> bool {
let k0 = unsafe { k0.as_ref() };
let k1 = unsafe { k1.as_ref() };
k0 == k1
}
//
// Full viewing keys
//
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_clone(
key: *const FullViewingKey,
) -> *mut FullViewingKey {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(key.clone())))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_free(key: *mut FullViewingKey) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_parse(
stream: Option<StreamObj>,
read_cb: Option<ReadCb>,
) -> *mut FullViewingKey {
let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
match FullViewingKey::read(reader) {
Err(e) => {
error!(
"Stream failure reading bytes of Orchard full viewing key: {}",
e
);
std::ptr::null_mut()
}
Ok(fvk) => Box::into_raw(Box::new(fvk)),
}
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_serialize(
key: *const FullViewingKey,
stream: Option<StreamObj>,
write_cb: Option<WriteCb>,
) -> bool {
let key = unsafe { key.as_ref() }.expect("Orchard full viewing key pointer may not be null.");
let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
match key.write(&mut writer) {
Ok(()) => true,
Err(e) => {
error!("Stream failure writing Orchard full viewing key: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_to_incoming_viewing_key(
key: *const FullViewingKey,
) -> *mut IncomingViewingKey {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(IncomingViewingKey::from(key))))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_eq(
k0: *const FullViewingKey,
k1: *const FullViewingKey,
) -> bool {
let k0 = unsafe { k0.as_ref() };
let k1 = unsafe { k1.as_ref() };
k0 == k1
}
//
// Spending keys
//
#[no_mangle]
pub extern "C" fn orchard_spending_key_for_account(
seed: *const u8,
seed_len: usize,
bip44_coin_type: u32,
account_id: u32,
) -> *mut SpendingKey {
let seed = unsafe { slice::from_raw_parts(seed, seed_len) };
SpendingKey::from_zip32_seed(seed, bip44_coin_type, account_id)
.map(|key| Box::into_raw(Box::new(key)))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_spending_key_clone(key: *const SpendingKey) -> *mut SpendingKey {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(*key)))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_spending_key_free(key: *mut SpendingKey) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
#[no_mangle]
pub extern "C" fn orchard_spending_key_parse(
stream: Option<StreamObj>,
read_cb: Option<ReadCb>,
) -> *mut SpendingKey {
let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
let mut buf = [0u8; 32];
match reader.read_exact(&mut buf) {
Err(e) => {
error!(
"Stream failure reading bytes of Orchard spending key: {}",
e
);
std::ptr::null_mut()
}
Ok(()) => {
let sk_opt = SpendingKey::from_bytes(buf);
if sk_opt.is_some().into() {
Box::into_raw(Box::new(sk_opt.unwrap()))
} else {
error!("Failed to parse Orchard spending key.");
std::ptr::null_mut()
}
}
}
}
#[no_mangle]
pub extern "C" fn orchard_spending_key_serialize(
key: *const SpendingKey,
stream: Option<StreamObj>,
write_cb: Option<WriteCb>,
) -> bool {
let key = unsafe { key.as_ref() }.expect("Orchard spending key pointer may not be null.");
let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
match writer.write_all(key.to_bytes()) {
Ok(()) => true,
Err(e) => {
error!("Stream failure writing Orchard spending key: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_spending_key_to_full_viewing_key(
key: *const SpendingKey,
) -> *mut FullViewingKey {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(FullViewingKey::from(key))))
.unwrap_or(std::ptr::null_mut())
}

View File

@ -72,6 +72,7 @@ mod address_ffi;
mod history_ffi;
mod incremental_merkle_tree_ffi;
mod orchard_ffi;
mod orchard_keys_ffi;
mod transaction_ffi;
mod zip339_ffi;

View File

@ -0,0 +1,50 @@
// Copyright (c) 2021 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include <gtest/gtest.h>
#include "zcash/address/orchard.hpp"
TEST(OrchardZkeysTest, IVKSerializationRoundtrip) {
auto seed = MnemonicSeed::Random(1); //testnet coin type
auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0);
auto fvk = sk.ToFullViewingKey();
auto ivk = fvk.ToIncomingViewingKey();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << ivk;
auto ivk0 = libzcash::OrchardIncomingViewingKey::Read(ss);
ASSERT_EQ(ivk, ivk0);
}
TEST(OrchardZkeysTest, FVKSerializationRoundtrip) {
auto seed = MnemonicSeed::Random(1); //testnet coin type
auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0);
auto fvk = sk.ToFullViewingKey();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << fvk;
auto fvk0 = libzcash::OrchardFullViewingKey::Read(ss);
ASSERT_EQ(fvk, fvk0);
}
TEST(OrchardZkeysTest, SKSerializationRoundtrip) {
auto seed = MnemonicSeed::Random(1); //testnet coin type
auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
std::string skStr = ss.str();
auto sk0 = libzcash::OrchardSpendingKey::Read(ss);
CDataStream ss0(SER_NETWORK, PROTOCOL_VERSION);
ss0 << sk0;
std::string sk0Str = ss0.str();
ASSERT_EQ(skStr, sk0Str);
}

View File

@ -431,7 +431,7 @@ ZcashdUnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() {
}
}
std::optional<libzcash::ZcashdUnifiedSpendingKey> CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) {
std::optional<libzcash::ZcashdUnifiedSpendingKey> CWallet::GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId) {
auto seed = GetMnemonicSeed();
if (!seed.has_value()) {
throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed.");

View File

@ -1097,7 +1097,7 @@ public:
* Unified keys & addresses
*/
libzcash::ZcashdUnifiedSpendingKey GenerateNewUnifiedSpendingKey();
std::optional<libzcash::ZcashdUnifiedSpendingKey> GenerateUnifiedSpendingKeyForAccount(uint32_t accountId);
std::optional<libzcash::ZcashdUnifiedSpendingKey> GenerateUnifiedSpendingKeyForAccount(libzcash::AccountId accountId);
/**
* Increment the next transaction order id

View File

@ -2,6 +2,7 @@
#define ZC_ADDRESS_H_
#include "uint256.h"
#include "zcash/address/orchard.hpp"
#include "zcash/address/sapling.hpp"
#include "zcash/address/sprout.hpp"
#include "zcash/address/zip32.h"

View File

@ -0,0 +1,40 @@
// Copyright (c) 2021 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "zcash/address/orchard.hpp"
namespace libzcash {
OrchardRawAddress OrchardIncomingViewingKey::Address(const diversifier_index_t& j) const {
return OrchardRawAddress(orchard_incoming_viewing_key_to_address(inner.get(), j.begin()));
}
OrchardIncomingViewingKey OrchardFullViewingKey::ToIncomingViewingKey() const {
return OrchardIncomingViewingKey(orchard_full_viewing_key_to_incoming_viewing_key(inner.get()));
}
OrchardSpendingKey OrchardSpendingKey::ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
libzcash::AccountId accountId) {
auto ptr = orchard_spending_key_for_account(
seed.RawSeed().data(),
seed.RawSeed().size(),
bip44CoinType,
accountId);
if (ptr == nullptr) {
throw std::ios_base::failure("Unable to generate Orchard extended spending key.");
}
return OrchardSpendingKey(ptr);
}
OrchardFullViewingKey OrchardSpendingKey::ToFullViewingKey() const {
return OrchardFullViewingKey(orchard_spending_key_to_full_viewing_key(inner.get()));
}
} //namespace libzcash

View File

@ -0,0 +1,257 @@
// Copyright (c) 2021 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_ADDRESS_ORCHARD_H
#define ZCASH_ADDRESS_ORCHARD_H
#include "streams.h"
#include "zcash/address/zip32.h"
#include <rust/orchard/keys.h>
namespace libzcash {
class OrchardIncomingViewingKey;
class OrchardRawAddress
{
private:
std::unique_ptr<OrchardRawAddressPtr, decltype(&orchard_address_free)> inner;
OrchardRawAddress() : inner(nullptr, orchard_address_free) {}
OrchardRawAddress(OrchardRawAddressPtr* ptr) : inner(ptr, orchard_address_free) {}
friend class OrchardIncomingViewingKey;
public:
OrchardRawAddress(OrchardRawAddress&& key) : inner(std::move(key.inner)) {}
OrchardRawAddress(const OrchardRawAddress& key) :
inner(orchard_address_clone(key.inner.get()), orchard_address_free) {}
OrchardRawAddress& operator=(OrchardRawAddress&& key)
{
if (this != &key) {
inner = std::move(key.inner);
}
return *this;
}
OrchardRawAddress& operator=(const OrchardRawAddress& key)
{
if (this != &key) {
inner.reset(orchard_address_clone(key.inner.get()));
}
return *this;
}
};
class OrchardFullViewingKey;
class OrchardIncomingViewingKey
{
private:
std::unique_ptr<OrchardIncomingViewingKeyPtr, decltype(&orchard_incoming_viewing_key_free)> inner;
OrchardIncomingViewingKey() : inner(nullptr, orchard_incoming_viewing_key_free) {}
OrchardIncomingViewingKey(OrchardIncomingViewingKeyPtr* key) :
inner(key, orchard_incoming_viewing_key_free) {}
friend class OrchardFullViewingKey;
public:
OrchardIncomingViewingKey(OrchardIncomingViewingKey&& key) : inner(std::move(key.inner)) {}
OrchardIncomingViewingKey(const OrchardIncomingViewingKey& key) :
inner(orchard_incoming_viewing_key_clone(key.inner.get()), orchard_incoming_viewing_key_free) {}
OrchardRawAddress Address(const diversifier_index_t& j) const;
OrchardIncomingViewingKey& operator=(OrchardIncomingViewingKey&& key)
{
if (this != &key) {
inner = std::move(key.inner);
}
return *this;
}
OrchardIncomingViewingKey& operator=(const OrchardIncomingViewingKey& key)
{
if (this != &key) {
inner.reset(orchard_incoming_viewing_key_clone(key.inner.get()));
}
return *this;
}
friend bool operator==(const OrchardIncomingViewingKey& a, const OrchardIncomingViewingKey& b)
{
return orchard_incoming_viewing_key_eq(a.inner.get(), b.inner.get());
}
friend bool operator<(const OrchardIncomingViewingKey& c1, const OrchardIncomingViewingKey& c2) {
return orchard_incoming_viewing_key_lt(c1.inner.get(), c2.inner.get());
}
template<typename Stream>
void Serialize(Stream& s) const {
RustStream rs(s);
if (!orchard_incoming_viewing_key_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
throw std::ios_base::failure("Failed to serialize Orchard incoming viewing key");
}
}
template<typename Stream>
void Unserialize(Stream& s) {
RustStream rs(s);
OrchardIncomingViewingKeyPtr* key = orchard_incoming_viewing_key_parse(&rs, RustStream<Stream>::read_callback);
if (key == nullptr) {
throw std::ios_base::failure("Failed to parse Orchard incoming viewing key");
}
inner.reset(key);
}
template<typename Stream>
static OrchardIncomingViewingKey Read(Stream& stream) {
OrchardIncomingViewingKey key;
stream >> key;
return key;
}
};
class OrchardSpendingKey;
class OrchardFullViewingKey
{
private:
std::unique_ptr<OrchardFullViewingKeyPtr, decltype(&orchard_full_viewing_key_free)> inner;
OrchardFullViewingKey() : inner(nullptr, orchard_full_viewing_key_free) {}
OrchardFullViewingKey(OrchardFullViewingKeyPtr* ptr) :
inner(ptr, orchard_full_viewing_key_free) {}
friend class OrchardSpendingKey;
public:
OrchardFullViewingKey(OrchardFullViewingKey&& key) : inner(std::move(key.inner)) {}
OrchardFullViewingKey(const OrchardFullViewingKey& key) :
inner(orchard_full_viewing_key_clone(key.inner.get()), orchard_full_viewing_key_free) {}
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
{
if (this != &key) {
inner = std::move(key.inner);
}
return *this;
}
OrchardFullViewingKey& operator=(const OrchardFullViewingKey& key)
{
if (this != &key) {
inner.reset(orchard_full_viewing_key_clone(key.inner.get()));
}
return *this;
}
friend bool operator==(const OrchardFullViewingKey& a, const OrchardFullViewingKey& b)
{
return orchard_full_viewing_key_eq(a.inner.get(), b.inner.get());
}
template<typename Stream>
void Serialize(Stream& s) const {
RustStream rs(s);
if (!orchard_full_viewing_key_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
throw std::ios_base::failure("Failed to serialize Orchard full viewing key");
}
}
template<typename Stream>
void Unserialize(Stream& s) {
RustStream rs(s);
OrchardFullViewingKeyPtr* key = orchard_full_viewing_key_parse(&rs, RustStream<Stream>::read_callback);
if (key == nullptr) {
throw std::ios_base::failure("Failed to parse Orchard full viewing key");
}
inner.reset(key);
}
template<typename Stream>
static OrchardFullViewingKey Read(Stream& stream) {
OrchardFullViewingKey key;
stream >> key;
return key;
}
OrchardIncomingViewingKey ToIncomingViewingKey() const;
};
class OrchardSpendingKey
{
private:
std::unique_ptr<OrchardSpendingKeyPtr, decltype(&orchard_spending_key_free)> inner;
OrchardSpendingKey() : inner(nullptr, orchard_spending_key_free) {}
OrchardSpendingKey(OrchardSpendingKeyPtr* ptr) :
inner(ptr, orchard_spending_key_free) {}
public:
OrchardSpendingKey(OrchardSpendingKey&& key) : inner(std::move(key.inner)) {}
OrchardSpendingKey(const OrchardSpendingKey& key) :
inner(orchard_spending_key_clone(key.inner.get()), orchard_spending_key_free) {}
static OrchardSpendingKey ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
libzcash::AccountId accountId);
OrchardSpendingKey& operator=(OrchardSpendingKey&& key)
{
if (this != &key) {
inner = std::move(key.inner);
}
return *this;
}
OrchardSpendingKey& operator=(const OrchardSpendingKey& key)
{
if (this != &key) {
inner.reset(orchard_spending_key_clone(key.inner.get()));
}
return *this;
}
template<typename Stream>
void Serialize(Stream& s) const {
RustStream rs(s);
if (!orchard_spending_key_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
throw std::ios_base::failure("Failed to serialize Orchard spending key");
}
}
template<typename Stream>
void Unserialize(Stream& s) {
RustStream rs(s);
OrchardSpendingKeyPtr* key = orchard_spending_key_parse(&rs, RustStream<Stream>::read_callback);
if (key == nullptr) {
throw std::ios_base::failure("Failed to parse Orchard spending key");
}
inner.reset(key);
}
template<typename Stream>
static OrchardSpendingKey Read(Stream& stream) {
OrchardSpendingKey key;
stream >> key;
return key;
}
OrchardFullViewingKey ToFullViewingKey() const;
};
} // namespace libzcash
#endif // ZCASH_ADDRESS_ORCHARD_H

View File

@ -85,7 +85,7 @@ std::optional<unsigned int> diversifier_index_t::ToTransparentChildIndex() const
// Transparent
//
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) {
auto rawSeed = seed.RawSeed();
auto m = CExtKey::Master(rawSeed.data(), rawSeed.size());
@ -110,7 +110,7 @@ std::optional<std::pair<CExtKey, HDKeyPath>> DeriveBip44TransparentAccountKey(co
std::optional<Bip44AccountChains> Bip44AccountChains::ForAccount(
const MnemonicSeed& seed,
uint32_t bip44CoinType,
uint32_t accountId) {
libzcash::AccountId accountId) {
auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId);
if (!accountKeyOpt.has_value()) return std::nullopt;
@ -251,7 +251,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const
return xsk_i;
}
std::pair<SaplingExtendedSpendingKey, HDKeyPath> SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
std::pair<SaplingExtendedSpendingKey, HDKeyPath> SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) {
auto m = Master(seed);
// We use a fixed keypath scheme of m/32'/coin_type'/account'
@ -313,7 +313,7 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const
// Unified
//
std::optional<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
std::optional<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) {
ZcashdUnifiedSpendingKey usk;
usk.accountId = accountId;

View File

@ -165,6 +165,8 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed);
namespace libzcash {
typedef uint32_t AccountId;
/**
* 88-bit diversifier index. This would ideally derive from base_uint
* but those values must have bit widths that are multiples of 32.
@ -290,7 +292,7 @@ struct SaplingExtendedSpendingKey {
}
static SaplingExtendedSpendingKey Master(const HDSeed& seed);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t accountId);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex);
@ -363,7 +365,7 @@ public:
class ZcashdUnifiedSpendingKey {
private:
uint32_t accountId;
libzcash::AccountId accountId;
std::optional<CExtKey> transparentKey;
std::optional<SaplingExtendedSpendingKey> saplingKey;
@ -372,7 +374,7 @@ public:
static std::optional<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> ForAccount(
const MnemonicSeed& mnemonic,
uint32_t bip44CoinType,
uint32_t accountId);
libzcash::AccountId accountId);
const std::optional<CExtKey>& GetTransparentKey() const {
return transparentKey;
@ -390,18 +392,18 @@ std::optional<unsigned long> ParseHDKeypathAccount(uint32_t purpose, uint32_t co
class Bip44AccountChains {
private:
uint256 seedFp;
uint32_t accountId;
libzcash::AccountId accountId;
uint32_t bip44CoinType;
CExtKey external;
CExtKey internal;
Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn):
Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn):
seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {}
public:
static std::optional<Bip44AccountChains> ForAccount(
const MnemonicSeed& mnemonic,
uint32_t bip44CoinType,
uint32_t accountId);
libzcash::AccountId accountId);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveExternal(uint32_t addrIndex);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveInternal(uint32_t addrIndex);