Merge pull request #6459 from str4d/zcash_primitives-0.10
Migrate to `zcash_primitives 0.10`
This commit is contained in:
commit
6ebf01fa83
|
@ -2363,9 +2363,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.9.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f9a45953c4ddd81d68f45920955707f45c8926800671f354dd13b97507edf28"
|
checksum = "ec8aed1d098e9f1b2bcd957ceab4188bf343cea30e7d0327fa49cea6ec44b167"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bip0039",
|
"bip0039",
|
||||||
|
@ -2399,9 +2399,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77381adc72286874e563ee36ba99953946abcbd195ada45440a2754ca823d407"
|
checksum = "28ca180a8138ae6e2de2b88573ed19dd57798f42a79a00d992b4d727132c7081"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
|
|
|
@ -61,8 +61,8 @@ zcash_address = "0.2"
|
||||||
zcash_encoding = "0.2"
|
zcash_encoding = "0.2"
|
||||||
zcash_history = "0.3"
|
zcash_history = "0.3"
|
||||||
zcash_note_encryption = "0.2"
|
zcash_note_encryption = "0.2"
|
||||||
zcash_primitives = { version = "0.9", features = ["transparent-inputs"] }
|
zcash_primitives = { version = "0.10.2", features = ["temporary-zcashd", "transparent-inputs"] }
|
||||||
zcash_proofs = { version = "0.9", features = ["directories"] }
|
zcash_proofs = { version = "0.10", features = ["directories"] }
|
||||||
ed25519-zebra = "3"
|
ed25519-zebra = "3"
|
||||||
zeroize = "1.4.2"
|
zeroize = "1.4.2"
|
||||||
|
|
||||||
|
@ -112,4 +112,3 @@ features = ["ansi", "env-filter", "fmt", "time"]
|
||||||
lto = 'thin'
|
lto = 'thin'
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
|
|
@ -966,6 +966,30 @@ criteria = "safe-to-deploy"
|
||||||
delta = "0.8.1 -> 0.9.1"
|
delta = "0.8.1 -> 0.9.1"
|
||||||
notes = "The ECC core team maintains this crate, and we have reviewed every line."
|
notes = "The ECC core team maintains this crate, and we have reviewed every line."
|
||||||
|
|
||||||
|
[[audits.zcash_primitives]]
|
||||||
|
who = "Jack Grigg <jack@electriccoin.co>"
|
||||||
|
criteria = ["safe-to-deploy", "crypto-reviewed"]
|
||||||
|
delta = "0.9.1 -> 0.10.0"
|
||||||
|
notes = "The ECC core team maintains this crate, and we have reviewed every line."
|
||||||
|
|
||||||
|
[[audits.zcash_primitives]]
|
||||||
|
who = "Jack Grigg <jack@electriccoin.co>"
|
||||||
|
criteria = ["safe-to-deploy", "crypto-reviewed"]
|
||||||
|
delta = "0.10.0 -> 0.10.1"
|
||||||
|
notes = """
|
||||||
|
The ECC core team maintains this crate, and we have reviewed every line.
|
||||||
|
This point release temporarily re-exposes some constructors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[audits.zcash_primitives]]
|
||||||
|
who = "Jack Grigg <jack@electriccoin.co>"
|
||||||
|
criteria = ["safe-to-deploy", "crypto-reviewed"]
|
||||||
|
delta = "0.10.1 -> 0.10.2"
|
||||||
|
notes = """
|
||||||
|
The ECC core team maintains this crate, and we have reviewed every line.
|
||||||
|
This point release temporarily re-exposes a constructor.
|
||||||
|
"""
|
||||||
|
|
||||||
[[audits.zcash_proofs]]
|
[[audits.zcash_proofs]]
|
||||||
who = "Jack Grigg <jack@z.cash>"
|
who = "Jack Grigg <jack@z.cash>"
|
||||||
criteria = ["crypto-reviewed", "safe-to-deploy"]
|
criteria = ["crypto-reviewed", "safe-to-deploy"]
|
||||||
|
@ -994,6 +1018,12 @@ who = "Jack Grigg <jack@z.cash>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
delta = "0.8.0 -> 0.9.0"
|
delta = "0.8.0 -> 0.9.0"
|
||||||
|
|
||||||
|
[[audits.zcash_proofs]]
|
||||||
|
who = "Jack Grigg <jack@electriccoin.co>"
|
||||||
|
criteria = ["safe-to-deploy", "crypto-reviewed"]
|
||||||
|
delta = "0.9.0 -> 0.10.0"
|
||||||
|
notes = "The ECC core team maintains this crate, and we have reviewed every line."
|
||||||
|
|
||||||
[[audits.zeroize]]
|
[[audits.zeroize]]
|
||||||
who = "Daira Hopwood <daira@jacaranda.org>"
|
who = "Daira Hopwood <daira@jacaranda.org>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "transaction_builder.h"
|
#include "transaction_builder.h"
|
||||||
#include "gtest/utils.h"
|
#include "gtest/utils.h"
|
||||||
|
#include "test/test_util.h"
|
||||||
#include "util/test.h"
|
#include "util/test.h"
|
||||||
#include "zcash/JoinSplit.hpp"
|
#include "zcash/JoinSplit.hpp"
|
||||||
|
|
||||||
|
@ -1150,7 +1151,7 @@ TEST(ChecktransactionTests, InvalidSaplingShieldedCoinbase) {
|
||||||
// Make it an invalid shielded coinbase (no ciphertexts or commitments).
|
// Make it an invalid shielded coinbase (no ciphertexts or commitments).
|
||||||
mtx.vin.resize(1);
|
mtx.vin.resize(1);
|
||||||
mtx.vin[0].prevout.SetNull();
|
mtx.vin[0].prevout.SetNull();
|
||||||
mtx.vShieldedOutput.resize(1);
|
mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription());
|
||||||
mtx.vJoinSplit.resize(0);
|
mtx.vJoinSplit.resize(0);
|
||||||
|
|
||||||
CTransaction tx(mtx);
|
CTransaction tx(mtx);
|
||||||
|
|
48
src/main.cpp
48
src/main.cpp
|
@ -981,6 +981,37 @@ bool ContextualCheckTransaction(
|
||||||
// https://zips.z.cash/zip-0213#specification
|
// https://zips.z.cash/zip-0213#specification
|
||||||
uint256 ovk;
|
uint256 ovk;
|
||||||
for (const OutputDescription &output : tx.vShieldedOutput) {
|
for (const OutputDescription &output : tx.vShieldedOutput) {
|
||||||
|
bool zip_212_enabled;
|
||||||
|
libzcash::SaplingPaymentAddress zaddr;
|
||||||
|
CAmount value;
|
||||||
|
|
||||||
|
// EoS height for 5.3.3 and 5.4.2 is 2121024 (mainnet).
|
||||||
|
// On testnet this height will be in the past, as of the 5.5.0 release.
|
||||||
|
if (nHeight >= 2121200) {
|
||||||
|
try {
|
||||||
|
auto decrypted = wallet::try_sapling_output_recovery(
|
||||||
|
*chainparams.RustNetwork(),
|
||||||
|
nHeight,
|
||||||
|
ovk.GetRawBytes(),
|
||||||
|
{
|
||||||
|
output.cv.GetRawBytes(),
|
||||||
|
output.cmu.GetRawBytes(),
|
||||||
|
output.ephemeralKey.GetRawBytes(),
|
||||||
|
output.encCiphertext,
|
||||||
|
output.outCiphertext,
|
||||||
|
});
|
||||||
|
zip_212_enabled = decrypted->zip_212_enabled();
|
||||||
|
|
||||||
|
libzcash::SaplingNotePlaintext notePt;
|
||||||
|
std::tie(notePt, zaddr) = SaplingNotePlaintext::from_rust(std::move(decrypted));
|
||||||
|
value = notePt.value();
|
||||||
|
} catch (const rust::Error &e) {
|
||||||
|
return state.DoS(
|
||||||
|
DOS_LEVEL_BLOCK,
|
||||||
|
error("ContextualCheckTransaction(): failed to recover plaintext of coinbase output description"),
|
||||||
|
REJECT_INVALID, "bad-cb-output-desc-invalid-outct");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
auto outPlaintext = SaplingOutgoingPlaintext::decrypt(
|
auto outPlaintext = SaplingOutgoingPlaintext::decrypt(
|
||||||
output.outCiphertext, ovk, output.cv, output.cmu, output.ephemeralKey);
|
output.outCiphertext, ovk, output.cv, output.cmu, output.ephemeralKey);
|
||||||
if (!outPlaintext) {
|
if (!outPlaintext) {
|
||||||
|
@ -1007,12 +1038,20 @@ bool ContextualCheckTransaction(
|
||||||
REJECT_INVALID, "bad-cb-output-desc-invalid-encct");
|
REJECT_INVALID, "bad-cb-output-desc-invalid-encct");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto leadByte = encPlaintext->get_leadbyte();
|
||||||
|
assert(leadByte == 0x01 || leadByte == 0x02);
|
||||||
|
zip_212_enabled = (leadByte == 0x02);
|
||||||
|
|
||||||
|
zaddr = libzcash::SaplingPaymentAddress(encPlaintext->d, outPlaintext->pk_d);
|
||||||
|
value = encPlaintext->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
// ZIP 207: detect shielded funding stream elements
|
// ZIP 207: detect shielded funding stream elements
|
||||||
if (canopyActive) {
|
if (canopyActive) {
|
||||||
libzcash::SaplingPaymentAddress zaddr(encPlaintext->d, outPlaintext->pk_d);
|
|
||||||
for (auto it = fundingStreamElements.begin(); it != fundingStreamElements.end(); ++it) {
|
for (auto it = fundingStreamElements.begin(); it != fundingStreamElements.end(); ++it) {
|
||||||
const libzcash::SaplingPaymentAddress* streamAddr = std::get_if<libzcash::SaplingPaymentAddress>(&(it->first));
|
const libzcash::SaplingPaymentAddress* streamAddr = std::get_if<libzcash::SaplingPaymentAddress>(&(it->first));
|
||||||
if (streamAddr && zaddr == *streamAddr && encPlaintext->value() == it->second) {
|
if (streamAddr && zaddr == *streamAddr && value == it->second) {
|
||||||
fundingStreamElements.erase(it);
|
fundingStreamElements.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1024,15 +1063,14 @@ bool ContextualCheckTransaction(
|
||||||
// to 0x02. This applies even during the grace period, and also applies to
|
// to 0x02. This applies even during the grace period, and also applies to
|
||||||
// funding stream outputs sent to shielded payment addresses, if any.
|
// funding stream outputs sent to shielded payment addresses, if any.
|
||||||
// https://zips.z.cash/zip-0212#consensus-rule-change-for-coinbase-transactions
|
// https://zips.z.cash/zip-0212#consensus-rule-change-for-coinbase-transactions
|
||||||
auto leadByte = encPlaintext->get_leadbyte();
|
if (canopyActive != zip_212_enabled) {
|
||||||
assert(leadByte == 0x01 || leadByte == 0x02);
|
|
||||||
if (canopyActive != (leadByte == 0x02)) {
|
|
||||||
return state.DoS(
|
return state.DoS(
|
||||||
DOS_LEVEL_BLOCK,
|
DOS_LEVEL_BLOCK,
|
||||||
error("ContextualCheckTransaction(): coinbase output description has invalid note plaintext version"),
|
error("ContextualCheckTransaction(): coinbase output description has invalid note plaintext version"),
|
||||||
REJECT_INVALID,
|
REJECT_INVALID,
|
||||||
"bad-cb-output-desc-invalid-note-plaintext-version");
|
"bad-cb-output-desc-invalid-note-plaintext-version");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,7 +19,7 @@ use zcash_primitives::{
|
||||||
memo::{Memo, MemoBytes},
|
memo::{Memo, MemoBytes},
|
||||||
sapling::note_encryption::SaplingDomain,
|
sapling::note_encryption::SaplingDomain,
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{orchard as orchard_serialization, sapling, transparent, Amount},
|
components::{sapling, transparent, Amount},
|
||||||
sighash::{signature_hash, SignableInput, TransparentAuthorizingContext},
|
sighash::{signature_hash, SignableInput, TransparentAuthorizingContext},
|
||||||
txid::TxIdDigester,
|
txid::TxIdDigester,
|
||||||
Authorization, Transaction, TransactionData, TxId, TxVersion,
|
Authorization, Transaction, TransactionData, TxId, TxVersion,
|
||||||
|
@ -139,44 +139,6 @@ impl transparent::MapAuth<transparent::Authorized, TransparentAuth> for MapTrans
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move these trait impls into `zcash_primitives` so they can be on `()`.
|
|
||||||
struct IdentityMap;
|
|
||||||
|
|
||||||
impl sapling::MapAuth<sapling::Authorized, sapling::Authorized> for IdentityMap {
|
|
||||||
fn map_proof(
|
|
||||||
&self,
|
|
||||||
p: <sapling::Authorized as sapling::Authorization>::Proof,
|
|
||||||
) -> <sapling::Authorized as sapling::Authorization>::Proof {
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_auth_sig(
|
|
||||||
&self,
|
|
||||||
s: <sapling::Authorized as sapling::Authorization>::AuthSig,
|
|
||||||
) -> <sapling::Authorized as sapling::Authorization>::AuthSig {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl orchard_serialization::MapAuth<orchard::bundle::Authorized, orchard::bundle::Authorized>
|
|
||||||
for IdentityMap
|
|
||||||
{
|
|
||||||
fn map_spend_auth(
|
|
||||||
&self,
|
|
||||||
s: <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth,
|
|
||||||
) -> <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct PrecomputedAuth;
|
pub(crate) struct PrecomputedAuth;
|
||||||
|
|
||||||
impl Authorization for PrecomputedAuth {
|
impl Authorization for PrecomputedAuth {
|
||||||
|
@ -230,8 +192,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
let tx = Transaction::read(&buf[..], tx.consensus_branch_id()).unwrap();
|
let tx = Transaction::read(&buf[..], tx.consensus_branch_id()).unwrap();
|
||||||
|
|
||||||
let tx: TransactionData<PrecomputedAuth> =
|
let tx: TransactionData<PrecomputedAuth> =
|
||||||
tx.into_data()
|
tx.into_data().map_authorization(f_transparent, (), ());
|
||||||
.map_authorization(f_transparent, IdentityMap, IdentityMap);
|
|
||||||
let txid_parts = tx.digest(TxIdDigester);
|
let txid_parts = tx.digest(TxIdDigester);
|
||||||
(tx, txid_parts)
|
(tx, txid_parts)
|
||||||
});
|
});
|
||||||
|
@ -412,23 +373,23 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bundle) = tx.sapling_bundle() {
|
if let Some(bundle) = tx.sapling_bundle() {
|
||||||
assert!(!(bundle.shielded_spends.is_empty() && bundle.shielded_outputs.is_empty()));
|
assert!(!(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()));
|
||||||
|
|
||||||
// TODO: Separate into checking proofs, signatures, and other structural details.
|
// TODO: Separate into checking proofs, signatures, and other structural details.
|
||||||
let mut ctx = SaplingVerificationContext::new(true);
|
let mut ctx = SaplingVerificationContext::new(true);
|
||||||
|
|
||||||
if !bundle.shielded_spends.is_empty() {
|
if !bundle.shielded_spends().is_empty() {
|
||||||
eprintln!(" - {} Sapling Spend(s)", bundle.shielded_spends.len());
|
eprintln!(" - {} Sapling Spend(s)", bundle.shielded_spends().len());
|
||||||
if let Some(sighash) = &common_sighash {
|
if let Some(sighash) = &common_sighash {
|
||||||
for (i, spend) in bundle.shielded_spends.iter().enumerate() {
|
for (i, spend) in bundle.shielded_spends().iter().enumerate() {
|
||||||
if !ctx.check_spend(
|
if !ctx.check_spend(
|
||||||
spend.cv,
|
spend.cv(),
|
||||||
spend.anchor,
|
*spend.anchor(),
|
||||||
&spend.nullifier.0,
|
&spend.nullifier().0,
|
||||||
spend.rk.clone(),
|
spend.rk().clone(),
|
||||||
sighash.as_ref(),
|
sighash.as_ref(),
|
||||||
spend.spend_auth_sig,
|
*spend.spend_auth_sig(),
|
||||||
groth16::Proof::read(&spend.zkproof[..]).unwrap(),
|
groth16::Proof::read(&spend.zkproof()[..]).unwrap(),
|
||||||
&GROTH16_PARAMS.spend_vk,
|
&GROTH16_PARAMS.spend_vk,
|
||||||
) {
|
) {
|
||||||
eprintln!(" ⚠️ Spend {} is invalid", i);
|
eprintln!(" ⚠️ Spend {} is invalid", i);
|
||||||
|
@ -441,9 +402,9 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bundle.shielded_outputs.is_empty() {
|
if !bundle.shielded_outputs().is_empty() {
|
||||||
eprintln!(" - {} Sapling Output(s)", bundle.shielded_outputs.len());
|
eprintln!(" - {} Sapling Output(s)", bundle.shielded_outputs().len());
|
||||||
for (i, output) in bundle.shielded_outputs.iter().enumerate() {
|
for (i, output) in bundle.shielded_outputs().iter().enumerate() {
|
||||||
if is_coinbase {
|
if is_coinbase {
|
||||||
if let Some((params, addr_net)) = context
|
if let Some((params, addr_net)) = context
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -453,17 +414,17 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
&SaplingDomain::for_height(params, height.unwrap()),
|
&SaplingDomain::for_height(params, height.unwrap()),
|
||||||
&zcash_primitives::keys::OutgoingViewingKey([0; 32]),
|
&zcash_primitives::keys::OutgoingViewingKey([0; 32]),
|
||||||
output,
|
output,
|
||||||
&output.cv,
|
output.cv(),
|
||||||
&output.out_ciphertext,
|
output.out_ciphertext(),
|
||||||
) {
|
) {
|
||||||
if note.value == 0 {
|
if note.value().inner() == 0 {
|
||||||
eprintln!(" - Output {} (dummy output):", i);
|
eprintln!(" - Output {} (dummy output):", i);
|
||||||
} else {
|
} else {
|
||||||
let zaddr = ZcashAddress::from_sapling(addr_net, addr.to_bytes());
|
let zaddr = ZcashAddress::from_sapling(addr_net, addr.to_bytes());
|
||||||
|
|
||||||
eprintln!(" - Output {}:", i);
|
eprintln!(" - Output {}:", i);
|
||||||
eprintln!(" - {}", zaddr);
|
eprintln!(" - {}", zaddr);
|
||||||
eprintln!(" - {}", render_value(note.value));
|
eprintln!(" - {}", render_value(note.value().inner()));
|
||||||
}
|
}
|
||||||
eprintln!(" - {}", render_memo(memo));
|
eprintln!(" - {}", render_memo(memo));
|
||||||
} else {
|
} else {
|
||||||
|
@ -478,10 +439,10 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.check_output(
|
if !ctx.check_output(
|
||||||
output.cv,
|
output.cv(),
|
||||||
output.cmu,
|
*output.cmu(),
|
||||||
jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key.0).unwrap(),
|
jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key().0).unwrap(),
|
||||||
groth16::Proof::read(&output.zkproof[..]).unwrap(),
|
groth16::Proof::read(&output.zkproof()[..]).unwrap(),
|
||||||
&GROTH16_PARAMS.output_vk,
|
&GROTH16_PARAMS.output_vk,
|
||||||
) {
|
) {
|
||||||
eprintln!(" ⚠️ Output {} is invalid", i);
|
eprintln!(" ⚠️ Output {} is invalid", i);
|
||||||
|
@ -491,9 +452,9 @@ pub(crate) fn inspect(tx: Transaction, context: Option<Context>) {
|
||||||
|
|
||||||
if let Some(sighash) = &common_sighash {
|
if let Some(sighash) = &common_sighash {
|
||||||
if !ctx.final_check(
|
if !ctx.final_check(
|
||||||
bundle.value_balance,
|
*bundle.value_balance(),
|
||||||
sighash.as_ref(),
|
sighash.as_ref(),
|
||||||
bundle.authorization.binding_sig,
|
bundle.authorization().binding_sig,
|
||||||
) {
|
) {
|
||||||
eprintln!("⚠️ Sapling bindingSig is invalid");
|
eprintln!("⚠️ Sapling bindingSig is invalid");
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,14 +104,17 @@ extern "C" {
|
||||||
unsigned char *result
|
unsigned char *result
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Compute [sk] [8] P for some 32-byte
|
/// Compute KDF^Sapling(KA^Agree(sk, P), ephemeral_key).
|
||||||
/// point P, and 32-byte Fs. If P or sk
|
///
|
||||||
/// are invalid, returns false. Otherwise,
|
/// P and sk must point to 32-byte buffers. If P does not
|
||||||
/// the result is written to the 32-byte
|
/// represent a Jubjub point or sk does not represent a
|
||||||
/// `result` buffer.
|
/// canonical Jubjub scalar, this function returns false.
|
||||||
bool librustzcash_sapling_ka_agree(
|
/// Otherwise, it writes the result to the 32-byte `result`
|
||||||
|
/// buffer and returns true.
|
||||||
|
bool librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
const unsigned char *p,
|
const unsigned char *p,
|
||||||
const unsigned char *sk,
|
const unsigned char *sk,
|
||||||
|
const unsigned char *ephemeral_key,
|
||||||
unsigned char *result
|
unsigned char *result
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use group::GroupEncoding;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use zcash_note_encryption::{
|
use zcash_note_encryption::{
|
||||||
try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, ShieldedOutput, ENC_CIPHERTEXT_SIZE,
|
try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, ShieldedOutput, ENC_CIPHERTEXT_SIZE,
|
||||||
};
|
};
|
||||||
|
@ -9,6 +10,7 @@ use zcash_primitives::{
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
||||||
|
value::ValueCommitment,
|
||||||
SaplingIvk,
|
SaplingIvk,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -64,7 +66,7 @@ pub(crate) fn try_sapling_output_recovery(
|
||||||
) -> Result<Box<DecryptedSaplingOutput>, &'static str> {
|
) -> Result<Box<DecryptedSaplingOutput>, &'static str> {
|
||||||
let domain = SaplingDomain::for_height(*network, BlockHeight::from_u32(height));
|
let domain = SaplingDomain::for_height(*network, BlockHeight::from_u32(height));
|
||||||
|
|
||||||
let cv = Option::from(jubjub::ExtendedPoint::from_bytes(&output.cv))
|
let cv = Option::from(ValueCommitment::from_bytes_not_small_order(&output.cv))
|
||||||
.ok_or("Invalid output.cv passed to wallet::try_sapling_note_decryption()")?;
|
.ok_or("Invalid output.cv passed to wallet::try_sapling_note_decryption()")?;
|
||||||
|
|
||||||
let (note, recipient, memo) = try_output_recovery_with_ovk(
|
let (note, recipient, memo) = try_output_recovery_with_ovk(
|
||||||
|
@ -115,18 +117,18 @@ pub(crate) struct DecryptedSaplingOutput {
|
||||||
|
|
||||||
impl DecryptedSaplingOutput {
|
impl DecryptedSaplingOutput {
|
||||||
pub(crate) fn note_value(&self) -> u64 {
|
pub(crate) fn note_value(&self) -> u64 {
|
||||||
self.note.value
|
self.note.value().inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn note_rseed(&self) -> [u8; 32] {
|
pub(crate) fn note_rseed(&self) -> [u8; 32] {
|
||||||
match self.note.rseed {
|
match self.note.rseed() {
|
||||||
sapling::Rseed::BeforeZip212(rcm) => rcm.to_bytes(),
|
sapling::Rseed::BeforeZip212(rcm) => rcm.to_bytes(),
|
||||||
sapling::Rseed::AfterZip212(rseed) => rseed,
|
sapling::Rseed::AfterZip212(rseed) => *rseed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn zip_212_enabled(&self) -> bool {
|
pub(crate) fn zip_212_enabled(&self) -> bool {
|
||||||
matches!(self.note.rseed, sapling::Rseed::AfterZip212(_))
|
matches!(self.note.rseed(), sapling::Rseed::AfterZip212(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn recipient_d(&self) -> [u8; 11] {
|
pub(crate) fn recipient_d(&self) -> [u8; 11] {
|
||||||
|
@ -134,7 +136,7 @@ impl DecryptedSaplingOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn recipient_pk_d(&self) -> [u8; 32] {
|
pub(crate) fn recipient_pk_d(&self) -> [u8; 32] {
|
||||||
self.recipient.pk_d().to_bytes()
|
self.recipient.to_bytes()[11..].try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn memo(&self) -> [u8; 512] {
|
pub(crate) fn memo(&self) -> [u8; 512] {
|
||||||
|
|
|
@ -23,6 +23,7 @@ use bellman::groth16::{self, Parameters, PreparedVerifyingKey};
|
||||||
use blake2s_simd::Params as Blake2sParams;
|
use blake2s_simd::Params as Blake2sParams;
|
||||||
use bls12_381::Bls12;
|
use bls12_381::Bls12;
|
||||||
use group::{cofactor::CofactorGroup, GroupEncoding};
|
use group::{cofactor::CofactorGroup, GroupEncoding};
|
||||||
|
use incrementalmerkletree::Hashable;
|
||||||
use libc::{c_uchar, size_t};
|
use libc::{c_uchar, size_t};
|
||||||
use rand_core::{OsRng, RngCore};
|
use rand_core::{OsRng, RngCore};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -43,11 +44,19 @@ use std::ffi::OsString;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
|
||||||
|
use zcash_note_encryption::{Domain, EphemeralKeyBytes};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
|
consensus::Network,
|
||||||
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
|
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
|
||||||
|
merkle_tree::HashSer,
|
||||||
sapling::{
|
sapling::{
|
||||||
keys::FullViewingKey, merkle_hash, note_encryption::sapling_ka_agree, redjubjub, spend_sig,
|
keys::FullViewingKey,
|
||||||
Diversifier, Note, NullifierDerivingKey, Rseed,
|
merkle_hash,
|
||||||
|
note::{ExtractedNoteCommitment, NoteCommitment},
|
||||||
|
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
||||||
|
redjubjub, spend_sig,
|
||||||
|
value::NoteValue,
|
||||||
|
Diversifier, Node, Note, NullifierDerivingKey, PaymentAddress, Rseed, SaplingIvk,
|
||||||
},
|
},
|
||||||
zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address},
|
zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address},
|
||||||
};
|
};
|
||||||
|
@ -210,12 +219,12 @@ pub extern "C" fn librustzcash_init_zksnark_params(
|
||||||
/// `result` must be a valid pointer to 32 bytes which will be written.
|
/// `result` must be a valid pointer to 32 bytes which will be written.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
|
pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
|
||||||
let tmp = Note::uncommitted().to_bytes();
|
|
||||||
|
|
||||||
// Should be okay, caller is responsible for ensuring the pointer
|
// Should be okay, caller is responsible for ensuring the pointer
|
||||||
// is a valid pointer to 32 bytes that can be mutated.
|
// is a valid pointer to 32 bytes that can be mutated.
|
||||||
let result = unsafe { &mut *result };
|
let result = unsafe { &mut *result };
|
||||||
*result = tmp;
|
Node::empty_leaf()
|
||||||
|
.write(&mut result[..])
|
||||||
|
.expect("Sapling leaves are 32 bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a merkle tree hash for a given depth. The `depth` parameter should
|
/// Computes a merkle tree hash for a given depth. The `depth` parameter should
|
||||||
|
@ -363,26 +372,24 @@ fn priv_get_note(
|
||||||
value: u64,
|
value: u64,
|
||||||
rcm: *const [c_uchar; 32],
|
rcm: *const [c_uchar; 32],
|
||||||
) -> Result<Note, ()> {
|
) -> Result<Note, ()> {
|
||||||
let diversifier = Diversifier(unsafe { *diversifier });
|
let recipient_bytes = {
|
||||||
let g_d = diversifier.g_d().ok_or(())?;
|
let mut tmp = [0; 43];
|
||||||
|
tmp[..11].copy_from_slice(unsafe { &*diversifier });
|
||||||
let pk_d = de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*pk_d })).ok_or(())?;
|
tmp[11..].copy_from_slice(unsafe { &*pk_d });
|
||||||
|
tmp
|
||||||
let pk_d = de_ct(pk_d.into_subgroup()).ok_or(())?;
|
};
|
||||||
|
let recipient = PaymentAddress::from_bytes(&recipient_bytes).ok_or(())?;
|
||||||
|
|
||||||
// Deserialize randomness
|
// Deserialize randomness
|
||||||
// If this is after ZIP 212, the caller has calculated rcm, and we don't need to call
|
// If this is after ZIP 212, the caller has calculated rcm, and we don't need to call
|
||||||
// Note::derive_esk, so we just pretend the note was using this rcm all along.
|
// Note::derive_esk, so we just pretend the note was using this rcm all along.
|
||||||
let rseed = Rseed::BeforeZip212(de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })).ok_or(())?);
|
let rseed = Rseed::BeforeZip212(de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })).ok_or(())?);
|
||||||
|
|
||||||
let note = Note {
|
Ok(Note::from_parts(
|
||||||
value,
|
recipient,
|
||||||
g_d,
|
NoteValue::from_raw(value),
|
||||||
pk_d,
|
|
||||||
rseed,
|
rseed,
|
||||||
};
|
))
|
||||||
|
|
||||||
Ok(note)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a Sapling nullifier.
|
/// Compute a Sapling nullifier.
|
||||||
|
@ -440,45 +447,68 @@ pub extern "C" fn librustzcash_sapling_compute_cmu(
|
||||||
rcm: *const [c_uchar; 32],
|
rcm: *const [c_uchar; 32],
|
||||||
result: *mut [c_uchar; 32],
|
result: *mut [c_uchar; 32],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let note = match priv_get_note(diversifier, pk_d, value, rcm) {
|
let get_cm = || -> Result<NoteCommitment, ()> {
|
||||||
Ok(p) => p,
|
let diversifier = Diversifier(unsafe { *diversifier });
|
||||||
Err(_) => return false,
|
let g_d = diversifier.g_d().ok_or(())?;
|
||||||
|
|
||||||
|
let pk_d = de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*pk_d })).ok_or(())?;
|
||||||
|
let pk_d = de_ct(pk_d.into_subgroup()).ok_or(())?;
|
||||||
|
|
||||||
|
let rcm = de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })).ok_or(())?;
|
||||||
|
|
||||||
|
Ok(NoteCommitment::temporary_zcashd_derive(
|
||||||
|
g_d.to_bytes(),
|
||||||
|
pk_d.to_bytes(),
|
||||||
|
NoteValue::from_raw(value),
|
||||||
|
rcm,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let cmu = match get_cm() {
|
||||||
|
Ok(cm) => ExtractedNoteCommitment::from(cm),
|
||||||
|
Err(()) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = unsafe { &mut *result };
|
let result = unsafe { &mut *result };
|
||||||
*result = note.cmu().to_bytes();
|
*result = cmu.to_bytes();
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs.
|
/// Computes KDF^Sapling(KA^Agree(sk, P), ephemeral_key).
|
||||||
///
|
///
|
||||||
/// If P or sk are invalid, returns false. Otherwise, the result is written to
|
/// `p` and `sk` must point to 32-byte buffers. If `p` does not represent a compressed
|
||||||
/// the 32-byte `result` buffer.
|
/// Jubjub point or `sk` does not represent a canonical Jubjub scalar, this function
|
||||||
|
/// returns `false`. Otherwise, it writes the result to the 32-byte `result` buffer and
|
||||||
|
/// returns `true`.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn librustzcash_sapling_ka_agree(
|
pub extern "C" fn librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
p: *const [c_uchar; 32],
|
p: *const [c_uchar; 32],
|
||||||
sk: *const [c_uchar; 32],
|
sk: *const [c_uchar; 32],
|
||||||
|
ephemeral_key: *const [c_uchar; 32],
|
||||||
result: *mut [c_uchar; 32],
|
result: *mut [c_uchar; 32],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Deserialize p
|
// Deserialize p (representing either epk or pk_d; we can handle them identically).
|
||||||
let p = match de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*p })) {
|
let epk = match SaplingDomain::<Network>::epk(&EphemeralKeyBytes(unsafe { *p })) {
|
||||||
Some(p) => p,
|
Some(p) => SaplingDomain::<Network>::prepare_epk(p),
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize sk
|
// Deserialize sk (either ivk or esk; we can handle them identically).
|
||||||
let sk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*sk })) {
|
let ivk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*sk })) {
|
||||||
Some(p) => p,
|
Some(p) => PreparedIncomingViewingKey::new(&SaplingIvk(p)),
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute key agreement
|
// Compute key agreement
|
||||||
let ka = sapling_ka_agree(&sk, &p);
|
let secret = SaplingDomain::<Network>::ka_agree_dec(&ivk, &epk);
|
||||||
|
|
||||||
// Produce result
|
// Produce result
|
||||||
let result = unsafe { &mut *result };
|
let result = unsafe { &mut *result };
|
||||||
*result = ka.to_bytes();
|
result.clone_from_slice(
|
||||||
|
SaplingDomain::<Network>::kdf(secret, &EphemeralKeyBytes(unsafe { *ephemeral_key }))
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@ use zcash_note_encryption::EphemeralKeyBytes;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
merkle_tree::MerklePath,
|
merkle_tree::MerklePath,
|
||||||
sapling::{
|
sapling::{
|
||||||
|
note::ExtractedNoteCommitment,
|
||||||
redjubjub::{self, Signature},
|
redjubjub::{self, Signature},
|
||||||
|
value::ValueCommitment,
|
||||||
Diversifier, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
|
Diversifier, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
|
||||||
},
|
},
|
||||||
transaction::{
|
transaction::{
|
||||||
|
@ -164,7 +166,7 @@ impl BundleAssembler {
|
||||||
spend_auth_sig: &[u8; 64],
|
spend_auth_sig: &[u8; 64],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Deserialize the value commitment
|
// Deserialize the value commitment
|
||||||
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
|
let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
@ -188,14 +190,15 @@ impl BundleAssembler {
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.shielded_spends.push(sapling::SpendDescription {
|
self.shielded_spends
|
||||||
cv,
|
.push(sapling::SpendDescription::temporary_zcashd_from_parts(
|
||||||
anchor,
|
cv,
|
||||||
nullifier: Nullifier(nullifier),
|
anchor,
|
||||||
rk,
|
Nullifier(nullifier),
|
||||||
zkproof,
|
rk,
|
||||||
spend_auth_sig,
|
zkproof,
|
||||||
});
|
spend_auth_sig,
|
||||||
|
));
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -210,26 +213,26 @@ impl BundleAssembler {
|
||||||
zkproof: [u8; 192], // GROTH_PROOF_SIZE
|
zkproof: [u8; 192], // GROTH_PROOF_SIZE
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Deserialize the value commitment
|
// Deserialize the value commitment
|
||||||
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
|
let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize the commitment, which should be an element
|
// Deserialize the extracted note commitment.
|
||||||
// of Fr.
|
let cmu = match Option::from(ExtractedNoteCommitment::from_bytes(cm)) {
|
||||||
let cmu = match de_ct(bls12_381::Scalar::from_bytes(cm)) {
|
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.shielded_outputs.push(sapling::OutputDescription {
|
self.shielded_outputs
|
||||||
cv,
|
.push(sapling::OutputDescription::temporary_zcashd_from_parts(
|
||||||
cmu,
|
cv,
|
||||||
ephemeral_key: EphemeralKeyBytes(ephemeral_key),
|
cmu,
|
||||||
enc_ciphertext,
|
EphemeralKeyBytes(ephemeral_key),
|
||||||
out_ciphertext,
|
enc_ciphertext,
|
||||||
zkproof,
|
out_ciphertext,
|
||||||
});
|
zkproof,
|
||||||
|
));
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -244,12 +247,12 @@ fn finish_bundle_assembly(
|
||||||
let value_balance = Amount::from_i64(value_balance).expect("parsed elsewhere");
|
let value_balance = Amount::from_i64(value_balance).expect("parsed elsewhere");
|
||||||
let binding_sig = redjubjub::Signature::read(&binding_sig[..]).expect("parsed elsewhere");
|
let binding_sig = redjubjub::Signature::read(&binding_sig[..]).expect("parsed elsewhere");
|
||||||
|
|
||||||
Box::new(Bundle(sapling::Bundle {
|
Box::new(Bundle(sapling::Bundle::temporary_zcashd_from_parts(
|
||||||
shielded_spends: assembler.shielded_spends,
|
assembler.shielded_spends,
|
||||||
shielded_outputs: assembler.shielded_outputs,
|
assembler.shielded_outputs,
|
||||||
value_balance,
|
value_balance,
|
||||||
authorization: sapling::Authorized { binding_sig },
|
sapling::Authorized { binding_sig },
|
||||||
}))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Prover(SaplingProvingContext);
|
struct Prover(SaplingProvingContext);
|
||||||
|
@ -453,7 +456,7 @@ impl Verifier {
|
||||||
sighash_value: &[u8; 32],
|
sighash_value: &[u8; 32],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Deserialize the value commitment
|
// Deserialize the value commitment
|
||||||
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
|
let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
@ -484,7 +487,7 @@ impl Verifier {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.0.check_spend(
|
self.0.check_spend(
|
||||||
cv,
|
&cv,
|
||||||
anchor,
|
anchor,
|
||||||
nullifier,
|
nullifier,
|
||||||
rk,
|
rk,
|
||||||
|
@ -505,14 +508,13 @@ impl Verifier {
|
||||||
zkproof: &[u8; GROTH_PROOF_SIZE],
|
zkproof: &[u8; GROTH_PROOF_SIZE],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Deserialize the value commitment
|
// Deserialize the value commitment
|
||||||
let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) {
|
let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize the commitment, which should be an element
|
// Deserialize the extracted note commitment.
|
||||||
// of Fr.
|
let cmu = match Option::from(ExtractedNoteCommitment::from_bytes(cm)) {
|
||||||
let cm = match de_ct(bls12_381::Scalar::from_bytes(cm)) {
|
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
@ -530,8 +532,8 @@ impl Verifier {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.0.check_output(
|
self.0.check_output(
|
||||||
cv,
|
&cv,
|
||||||
cm,
|
cmu,
|
||||||
epk,
|
epk,
|
||||||
zkproof,
|
zkproof,
|
||||||
&prepare_verifying_key(
|
&prepare_verifying_key(
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use group::{Group, GroupEncoding};
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use group::Group;
|
||||||
use rand_core::{OsRng, RngCore};
|
use rand_core::{OsRng, RngCore};
|
||||||
use zcash_primitives::sapling::{Diversifier, NullifierDerivingKey, ViewingKey};
|
use zcash_primitives::sapling::{Diversifier, NullifierDerivingKey, ViewingKey};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
librustzcash_sapling_generate_r, librustzcash_sapling_ka_derive_symmetric_key,
|
||||||
librustzcash_sapling_ka_derivepublic,
|
librustzcash_sapling_ka_derivepublic,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,14 +41,8 @@ fn test_key_agreement() {
|
||||||
// we randomly generated
|
// we randomly generated
|
||||||
let mut shared_secret_sender = [0u8; 32];
|
let mut shared_secret_sender = [0u8; 32];
|
||||||
|
|
||||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
// Serialize pk_d for the call to librustzcash_sapling_ka_derive_symmetric_key
|
||||||
let addr_pk_d = addr.pk_d().to_bytes();
|
let addr_pk_d = addr.to_bytes()[11..].try_into().unwrap();
|
||||||
|
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
|
||||||
&addr_pk_d,
|
|
||||||
&esk,
|
|
||||||
&mut shared_secret_sender
|
|
||||||
));
|
|
||||||
|
|
||||||
// Create epk for the recipient, placed in the transaction. Computed
|
// Create epk for the recipient, placed in the transaction. Computed
|
||||||
// using the diversifier and esk.
|
// using the diversifier and esk.
|
||||||
|
@ -57,11 +53,19 @@ fn test_key_agreement() {
|
||||||
&mut epk
|
&mut epk
|
||||||
));
|
));
|
||||||
|
|
||||||
|
assert!(librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
|
&addr_pk_d,
|
||||||
|
&esk,
|
||||||
|
&epk,
|
||||||
|
&mut shared_secret_sender
|
||||||
|
));
|
||||||
|
|
||||||
// Create sharedSecret with ephemeral key
|
// Create sharedSecret with ephemeral key
|
||||||
let mut shared_secret_recipient = [0u8; 32];
|
let mut shared_secret_recipient = [0u8; 32];
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
assert!(librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
&epk,
|
&epk,
|
||||||
&ivk_serialized,
|
&ivk_serialized,
|
||||||
|
&epk,
|
||||||
&mut shared_secret_recipient
|
&mut shared_secret_recipient
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -685,7 +685,7 @@ fn key_components() {
|
||||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||||
|
|
||||||
let addr = fvk.to_payment_address(diversifier).unwrap();
|
let addr = fvk.to_payment_address(diversifier).unwrap();
|
||||||
assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
|
assert_eq!(&addr.to_bytes()[11..], &tv.default_pk_d);
|
||||||
{
|
{
|
||||||
let mut default_pk_d = [0u8; 32];
|
let mut default_pk_d = [0u8; 32];
|
||||||
librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d);
|
librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d);
|
||||||
|
@ -693,9 +693,7 @@ fn key_components() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let note_r = jubjub::Scalar::from_bytes(&tv.note_r).unwrap();
|
let note_r = jubjub::Scalar::from_bytes(&tv.note_r).unwrap();
|
||||||
let note = addr
|
let note = addr.create_note(tv.note_v, Rseed::BeforeZip212(note_r));
|
||||||
.create_note(tv.note_v, Rseed::BeforeZip212(note_r))
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(¬e.cmu().to_bytes(), &tv.note_cm);
|
assert_eq!(¬e.cmu().to_bytes(), &tv.note_cm);
|
||||||
|
|
||||||
assert_eq!(note.nf(&fvk.nk, tv.note_pos), Nullifier(tv.note_nf));
|
assert_eq!(note.nf(&fvk.nk, tv.note_pos), Nullifier(tv.note_nf));
|
||||||
|
|
|
@ -10,7 +10,7 @@ use zcash_primitives::{
|
||||||
consensus::BranchId,
|
consensus::BranchId,
|
||||||
legacy::Script,
|
legacy::Script,
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{orchard as orchard_serialization, sapling, transparent, Amount},
|
components::{sapling, transparent, Amount},
|
||||||
sighash::{SignableInput, TransparentAuthorizingContext},
|
sighash::{SignableInput, TransparentAuthorizingContext},
|
||||||
sighash_v5::v5_signature_hash,
|
sighash_v5::v5_signature_hash,
|
||||||
txid::TxIdDigester,
|
txid::TxIdDigester,
|
||||||
|
@ -102,44 +102,6 @@ impl transparent::MapAuth<transparent::Authorized, TransparentAuth> for MapTrans
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move these trait impls into `zcash_primitives` so they can be on `()`.
|
|
||||||
struct IdentityMap;
|
|
||||||
|
|
||||||
impl sapling::MapAuth<sapling::Authorized, sapling::Authorized> for IdentityMap {
|
|
||||||
fn map_proof(
|
|
||||||
&self,
|
|
||||||
p: <sapling::Authorized as sapling::Authorization>::Proof,
|
|
||||||
) -> <sapling::Authorized as sapling::Authorization>::Proof {
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_auth_sig(
|
|
||||||
&self,
|
|
||||||
s: <sapling::Authorized as sapling::Authorization>::AuthSig,
|
|
||||||
) -> <sapling::Authorized as sapling::Authorization>::AuthSig {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl orchard_serialization::MapAuth<orchard::bundle::Authorized, orchard::bundle::Authorized>
|
|
||||||
for IdentityMap
|
|
||||||
{
|
|
||||||
fn map_spend_auth(
|
|
||||||
&self,
|
|
||||||
s: <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth,
|
|
||||||
) -> <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct PrecomputedAuth;
|
pub(crate) struct PrecomputedAuth;
|
||||||
|
|
||||||
impl Authorization for PrecomputedAuth {
|
impl Authorization for PrecomputedAuth {
|
||||||
|
@ -240,9 +202,7 @@ pub extern "C" fn zcash_transaction_precomputed_init(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let tx = tx
|
let tx = tx.into_data().map_authorization(f_transparent, (), ());
|
||||||
.into_data()
|
|
||||||
.map_authorization(f_transparent, IdentityMap, IdentityMap);
|
|
||||||
|
|
||||||
let txid_parts = tx.digest(TxIdDigester);
|
let txid_parts = tx.digest(TxIdDigester);
|
||||||
Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts }))
|
Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts }))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
|
@ -8,7 +9,6 @@ use std::sync::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crossbeam_channel as channel;
|
use crossbeam_channel as channel;
|
||||||
use group::GroupEncoding;
|
|
||||||
use memuse::DynamicUsage;
|
use memuse::DynamicUsage;
|
||||||
use zcash_note_encryption::{batch, BatchDomain, Domain, ShieldedOutput, ENC_CIPHERTEXT_SIZE};
|
use zcash_note_encryption::{batch, BatchDomain, Domain, ShieldedOutput, ENC_CIPHERTEXT_SIZE};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
|
@ -711,7 +711,7 @@ impl BatchScanner {
|
||||||
block_tag,
|
block_tag,
|
||||||
txid,
|
txid,
|
||||||
|| SaplingDomain::for_height(params, height),
|
|| SaplingDomain::for_height(params, height),
|
||||||
&bundle.shielded_outputs,
|
bundle.shielded_outputs(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,7 +768,9 @@ impl BatchResult {
|
||||||
output: *output as u32,
|
output: *output as u32,
|
||||||
ivk: decrypted_note.ivk_tag,
|
ivk: decrypted_note.ivk_tag,
|
||||||
diversifier: decrypted_note.recipient.diversifier().0,
|
diversifier: decrypted_note.recipient.diversifier().0,
|
||||||
pk_d: decrypted_note.recipient.pk_d().to_bytes(),
|
pk_d: decrypted_note.recipient.to_bytes()[11..]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "test/test_bitcoin.h"
|
#include "test/test_bitcoin.h"
|
||||||
#include "test/test_util.h"
|
#include "test/test_util.h"
|
||||||
#include "util/system.h"
|
#include "util/system.h"
|
||||||
|
#include "util/test.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -149,23 +150,10 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co
|
||||||
if (tx.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
if (tx.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
||||||
tx.valueBalanceSapling = InsecureRandRange(100000000);
|
tx.valueBalanceSapling = InsecureRandRange(100000000);
|
||||||
for (int spend = 0; spend < shielded_spends; spend++) {
|
for (int spend = 0; spend < shielded_spends; spend++) {
|
||||||
SpendDescription sdesc;
|
tx.vShieldedSpend.push_back(RandomInvalidSpendDescription());
|
||||||
zcash_test_harness_random_jubjub_point(sdesc.cv.begin());
|
|
||||||
zcash_test_harness_random_jubjub_base(sdesc.anchor.begin());
|
|
||||||
sdesc.nullifier = InsecureRand256();
|
|
||||||
zcash_test_harness_random_jubjub_point(sdesc.rk.begin());
|
|
||||||
GetRandBytes(sdesc.zkproof.begin(), sdesc.zkproof.size());
|
|
||||||
tx.vShieldedSpend.push_back(sdesc);
|
|
||||||
}
|
}
|
||||||
for (int out = 0; out < shielded_outs; out++) {
|
for (int out = 0; out < shielded_outs; out++) {
|
||||||
OutputDescription odesc;
|
tx.vShieldedOutput.push_back(RandomInvalidOutputDescription());
|
||||||
zcash_test_harness_random_jubjub_point(odesc.cv.begin());
|
|
||||||
zcash_test_harness_random_jubjub_base(odesc.cmu.begin());
|
|
||||||
zcash_test_harness_random_jubjub_point(odesc.ephemeralKey.begin());
|
|
||||||
GetRandBytes(odesc.encCiphertext.begin(), odesc.encCiphertext.size());
|
|
||||||
GetRandBytes(odesc.outCiphertext.begin(), odesc.outCiphertext.size());
|
|
||||||
GetRandBytes(odesc.zkproof.begin(), odesc.zkproof.size());
|
|
||||||
tx.vShieldedOutput.push_back(odesc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We have removed pre-Sapling Sprout support.
|
// We have removed pre-Sapling Sprout support.
|
||||||
|
|
|
@ -309,8 +309,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact
|
||||||
CMutableTransaction newTx(tx);
|
CMutableTransaction newTx(tx);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
|
|
||||||
newTx.vShieldedSpend.push_back(SpendDescription());
|
newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription());
|
||||||
newTx.vShieldedSpend[0].nullifier = InsecureRand256();
|
|
||||||
|
|
||||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-no-sink-of-funds");
|
BOOST_CHECK(state.GetRejectReason() == "bad-txns-no-sink-of-funds");
|
||||||
|
@ -320,12 +319,11 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact
|
||||||
CMutableTransaction newTx(tx);
|
CMutableTransaction newTx(tx);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
|
|
||||||
newTx.vShieldedSpend.push_back(SpendDescription());
|
newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription());
|
||||||
newTx.vShieldedSpend[0].nullifier = InsecureRand256();
|
|
||||||
|
|
||||||
newTx.vShieldedOutput.push_back(OutputDescription());
|
newTx.vShieldedOutput.push_back(RandomInvalidOutputDescription());
|
||||||
|
|
||||||
newTx.vShieldedSpend.push_back(SpendDescription());
|
newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription());
|
||||||
newTx.vShieldedSpend[1].nullifier = newTx.vShieldedSpend[0].nullifier;
|
newTx.vShieldedSpend[1].nullifier = newTx.vShieldedSpend[0].nullifier;
|
||||||
|
|
||||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||||
|
@ -347,7 +345,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact
|
||||||
vout.nValue = 1;
|
vout.nValue = 1;
|
||||||
newTx.vout.push_back(vout);
|
newTx.vout.push_back(vout);
|
||||||
|
|
||||||
newTx.vShieldedSpend.push_back(SpendDescription());
|
newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription());
|
||||||
|
|
||||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||||
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-spend-description");
|
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-spend-description");
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <rust/ed25519.h>
|
#include <rust/ed25519.h>
|
||||||
|
#include <rust/test_harness.h>
|
||||||
|
|
||||||
// Sprout
|
// Sprout
|
||||||
CMutableTransaction GetValidSproutReceiveTransaction(
|
CMutableTransaction GetValidSproutReceiveTransaction(
|
||||||
|
@ -64,8 +65,7 @@ CMutableTransaction GetValidSproutReceiveTransaction(
|
||||||
// depend on this happening.
|
// depend on this happening.
|
||||||
if (version >= 4) {
|
if (version >= 4) {
|
||||||
// Shielded Output
|
// Shielded Output
|
||||||
OutputDescription od;
|
mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription());
|
||||||
mtx.vShieldedOutput.push_back(od);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
|
@ -334,6 +334,27 @@ CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore) {
|
||||||
return tsk;
|
return tsk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpendDescription RandomInvalidSpendDescription() {
|
||||||
|
SpendDescription sdesc;
|
||||||
|
zcash_test_harness_random_jubjub_point(sdesc.cv.begin());
|
||||||
|
zcash_test_harness_random_jubjub_base(sdesc.anchor.begin());
|
||||||
|
sdesc.nullifier = GetRandHash();
|
||||||
|
zcash_test_harness_random_jubjub_point(sdesc.rk.begin());
|
||||||
|
GetRandBytes(sdesc.zkproof.begin(), sdesc.zkproof.size());
|
||||||
|
return sdesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputDescription RandomInvalidOutputDescription() {
|
||||||
|
OutputDescription odesc;
|
||||||
|
zcash_test_harness_random_jubjub_point(odesc.cv.begin());
|
||||||
|
zcash_test_harness_random_jubjub_base(odesc.cmu.begin());
|
||||||
|
zcash_test_harness_random_jubjub_point(odesc.ephemeralKey.begin());
|
||||||
|
GetRandBytes(odesc.encCiphertext.begin(), odesc.encCiphertext.size());
|
||||||
|
GetRandBytes(odesc.outCiphertext.begin(), odesc.outCiphertext.size());
|
||||||
|
GetRandBytes(odesc.zkproof.begin(), odesc.zkproof.size());
|
||||||
|
return odesc;
|
||||||
|
}
|
||||||
|
|
||||||
TestSaplingNote GetTestSaplingNote(const libzcash::SaplingPaymentAddress& pa, CAmount value) {
|
TestSaplingNote GetTestSaplingNote(const libzcash::SaplingPaymentAddress& pa, CAmount value) {
|
||||||
// Generate dummy Sapling note
|
// Generate dummy Sapling note
|
||||||
libzcash::SaplingNote note(pa, value, libzcash::Zip212Enabled::BeforeZip212);
|
libzcash::SaplingNote note(pa, value, libzcash::Zip212Enabled::BeforeZip212);
|
||||||
|
|
|
@ -122,6 +122,9 @@ libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey();
|
||||||
|
|
||||||
CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore);
|
CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore);
|
||||||
|
|
||||||
|
SpendDescription RandomInvalidSpendDescription();
|
||||||
|
OutputDescription RandomInvalidOutputDescription();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a dummy SaplingNote and a SaplingMerkleTree with that note's commitment.
|
* Generate a dummy SaplingNote and a SaplingMerkleTree with that note's commitment.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -112,19 +112,20 @@ std::optional<SaplingEncCiphertext> SaplingNoteEncryption::encrypt_to_recipient(
|
||||||
throw std::logic_error("already encrypted to the recipient using this key");
|
throw std::logic_error("already encrypted to the recipient using this key");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 dhsecret;
|
// Construct the symmetric key
|
||||||
|
|
||||||
// The new consensus rules from ZIP 216 (https://zips.z.cash/zip-0216#specification)
|
// The new consensus rules from ZIP 216 (https://zips.z.cash/zip-0216#specification)
|
||||||
// on pk_d were enabled unconditionally, even before we started to apply them
|
// on pk_d were enabled unconditionally, even before we started to apply them
|
||||||
// retroactively.
|
// retroactively.
|
||||||
if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
|
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||||
|
if (!librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
|
pk_d.begin(),
|
||||||
|
esk.begin(),
|
||||||
|
epk.begin(),
|
||||||
|
K))
|
||||||
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the symmetric key
|
|
||||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
|
||||||
KDF_Sapling(K, dhsecret, epk);
|
|
||||||
|
|
||||||
// The nonce is zero because we never reuse keys
|
// The nonce is zero because we never reuse keys
|
||||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||||
|
|
||||||
|
@ -148,19 +149,19 @@ std::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
|
||||||
const uint256 &epk
|
const uint256 &epk
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint256 dhsecret;
|
// Construct the symmetric key.
|
||||||
|
|
||||||
// We consider ZIP 216 active all of the time because blocks prior to NU5
|
// We consider ZIP 216 active all of the time because blocks prior to NU5
|
||||||
// activation (on mainnet and testnet) did not contain Sapling transactions
|
// activation (on mainnet and testnet) did not contain Sapling transactions
|
||||||
// that violated its canonicity rule.
|
// that violated its canonicity rule.
|
||||||
if (!librustzcash_sapling_ka_agree(epk.begin(), ivk.begin(), dhsecret.begin())) {
|
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||||
|
if (!librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
|
epk.begin(),
|
||||||
|
ivk.begin(),
|
||||||
|
epk.begin(), K))
|
||||||
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the symmetric key
|
|
||||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
|
||||||
KDF_Sapling(K, dhsecret, epk);
|
|
||||||
|
|
||||||
// The nonce is zero because we never reuse keys
|
// The nonce is zero because we never reuse keys
|
||||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||||
|
|
||||||
|
@ -187,19 +188,20 @@ std::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
|
||||||
const uint256 &pk_d
|
const uint256 &pk_d
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint256 dhsecret;
|
// Construct the symmetric key.
|
||||||
|
|
||||||
// We consider ZIP 216 active all of the time because blocks prior to NU5
|
// We consider ZIP 216 active all of the time because blocks prior to NU5
|
||||||
// activation (on mainnet and testnet) did not contain Sapling transactions
|
// activation (on mainnet and testnet) did not contain Sapling transactions
|
||||||
// that violated its canonicity rule.
|
// that violated its canonicity rule.
|
||||||
if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
|
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||||
|
if (!librustzcash_sapling_ka_derive_symmetric_key(
|
||||||
|
pk_d.begin(),
|
||||||
|
esk.begin(),
|
||||||
|
epk.begin(),
|
||||||
|
K))
|
||||||
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the symmetric key
|
|
||||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
|
||||||
KDF_Sapling(K, dhsecret, epk);
|
|
||||||
|
|
||||||
// The nonce is zero because we never reuse keys
|
// The nonce is zero because we never reuse keys
|
||||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue