Delete Orchard-specific code, dependencies, and book pages

The tests do not compile as of this commit, due to Orchard-specific
constants being deleted, but everything else compiles.

Co-authored-by: ying tong <yingtong@z.cash>
This commit is contained in:
Jack Grigg 2022-01-27 18:36:03 +00:00
parent 159ab53da5
commit c2866beb47
67 changed files with 5 additions and 65497 deletions

View File

@ -22,35 +22,24 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"]
[dependencies]
aes = "0.7"
arrayvec = "0.7.0"
bigint = "4"
bitvec = "0.22"
blake2b_simd = "1"
ff = "0.11"
fpe = "0.5"
group = "0.11"
halo2 = "=0.1.0-beta.1"
lazy_static = "1"
memuse = { version = "0.2", features = ["nonempty"] }
pasta_curves = "0.2.1"
proptest = { version = "1.0.0", optional = true }
rand = "0.8"
reddsa = "0.1"
nonempty = "0.7"
serde = { version = "1.0", features = ["derive"] }
subtle = "2.3"
zcash_note_encryption = "0.1"
incrementalmerkletree = "0.2"
# Developer tooling dependencies
plotters = { version = "0.3.0", optional = true }
[dev-dependencies]
criterion = "0.3"
hex = "0.4"
proptest = "1.0.0"
zcash_note_encryption = { version = "0.1", features = ["pre-zip-212"] }
[target.'cfg(unix)'.dev-dependencies]
pprof = { version = "=0.6.1", features = ["criterion", "flamegraph"] }
@ -62,22 +51,10 @@ bench = false
dev-graph = ["halo2/dev-graph", "plotters"]
test-dependencies = ["proptest"]
[[bench]]
name = "note_decryption"
harness = false
[[bench]]
name = "primitives"
harness = false
[[bench]]
name = "small"
harness = false
[[bench]]
name = "circuit"
harness = false
[[bench]]
name = "poseidon"
harness = false

View File

@ -1,96 +0,0 @@
#[macro_use]
extern crate criterion;
use criterion::{BenchmarkId, Criterion};
#[cfg(unix)]
use pprof::criterion::{Output, PProfProfiler};
use orchard::{
builder::Builder,
bundle::Flags,
circuit::{ProvingKey, VerifyingKey},
keys::{FullViewingKey, SpendingKey},
value::NoteValue,
Anchor, Bundle,
};
use rand::rngs::OsRng;
fn criterion_benchmark(c: &mut Criterion) {
let rng = OsRng;
let sk = SpendingKey::from_bytes([7; 32]).unwrap();
let recipient = FullViewingKey::from(&sk).default_address();
let vk = VerifyingKey::build();
let pk = ProvingKey::build();
let create_bundle = |num_recipients| {
let mut builder = Builder::new(
Flags::from_parts(true, true),
Anchor::from_bytes([0; 32]).unwrap(),
);
for _ in 0..num_recipients {
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.unwrap();
}
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
let instances: Vec<_> = bundle
.actions()
.iter()
.map(|a| a.to_instance(*bundle.flags(), *bundle.anchor()))
.collect();
(bundle, instances)
};
let recipients_range = 1..=4;
{
let mut group = c.benchmark_group("proving");
group.sample_size(10);
for num_recipients in recipients_range.clone() {
let (bundle, instances) = create_bundle(num_recipients);
group.bench_function(BenchmarkId::new("bundle", num_recipients), |b| {
b.iter(|| {
bundle
.authorization()
.create_proof(&pk, &instances, rng)
.unwrap()
});
});
}
}
{
let mut group = c.benchmark_group("verifying");
for num_recipients in recipients_range {
let (bundle, instances) = create_bundle(num_recipients);
let bundle = bundle
.create_proof(&pk, rng)
.unwrap()
.apply_signatures(rng, [0; 32], &[])
.unwrap();
assert!(bundle.verify_proof(&vk).is_ok());
group.bench_function(BenchmarkId::new("bundle", num_recipients), |b| {
b.iter(|| bundle.authorization().proof().verify(&vk, &instances));
});
}
}
}
#[cfg(unix)]
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = criterion_benchmark
}
#[cfg(windows)]
criterion_group! {
name = benches;
config = Criterion::default();
targets = criterion_benchmark
}
criterion_main!(benches);

View File

@ -1,150 +0,0 @@
use std::array;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use orchard::{
builder::Builder,
bundle::Flags,
circuit::ProvingKey,
keys::{FullViewingKey, IncomingViewingKey, SpendingKey},
note_encryption::{CompactAction, OrchardDomain},
value::NoteValue,
Anchor, Bundle,
};
use rand::rngs::OsRng;
use zcash_note_encryption::{batch, try_compact_note_decryption, try_note_decryption};
#[cfg(unix)]
use pprof::criterion::{Output, PProfProfiler};
fn bench_note_decryption(c: &mut Criterion) {
let rng = OsRng;
let pk = ProvingKey::build();
let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap());
let valid_ivk = IncomingViewingKey::from(&fvk);
let recipient = fvk.default_address();
// Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption
// relies on an invalid ivk resulting in random noise for which the note commitment
// is invalid. However, in practice we still get early rejection:
// - The version byte will be invalid in 255/256 instances.
// - If the version byte is valid, one of either the note commitment check or the esk
// check will be invalid, saving us at least one scalar mul.
//
// Our fixed (action, invalid ivk) tuple will always fall into a specific rejection
// case. In order to reflect the real behaviour in the benchmarks, we trial-decrypt
// with 1000 invalid ivks (each of which will result in a different uniformly-random
// plaintext); this is equivalent to trial-decrypting 1000 different actions with the
// same ivk, but is faster to set up.
let invalid_ivks: Vec<_> = (0u32..1000)
.map(|i| {
let mut sk = [0; 32];
sk[..4].copy_from_slice(&i.to_le_bytes());
IncomingViewingKey::from(&FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap()))
})
.collect();
let bundle = {
let mut builder = Builder::new(
Flags::from_parts(true, true),
Anchor::from_bytes([0; 32]).unwrap(),
);
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.unwrap();
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
bundle
.create_proof(&pk, rng)
.unwrap()
.apply_signatures(rng, [0; 32], &[])
.unwrap()
};
let action = bundle.actions().first();
let domain = OrchardDomain::for_action(action);
let compact = {
let mut group = c.benchmark_group("note-decryption");
group.throughput(Throughput::Elements(1));
group.bench_function("valid", |b| {
b.iter(|| try_note_decryption(&domain, &valid_ivk, action).unwrap())
});
// Non-compact actions will always early-reject at the same point: AEAD decryption.
group.bench_function("invalid", |b| {
b.iter(|| try_note_decryption(&domain, &invalid_ivks[0], action))
});
let compact = CompactAction::from(action);
group.bench_function("compact-valid", |b| {
b.iter(|| try_compact_note_decryption(&domain, &valid_ivk, &compact).unwrap())
});
compact
};
{
let mut group = c.benchmark_group("compact-note-decryption");
group.throughput(Throughput::Elements(invalid_ivks.len() as u64));
group.bench_function("invalid", |b| {
b.iter(|| {
for ivk in &invalid_ivks {
try_compact_note_decryption(&domain, ivk, &compact);
}
})
});
}
{
// Benchmark with 2 IVKs to emulate a wallet with two pools of funds.
let ivks = 2;
let valid_ivks = vec![valid_ivk; ivks];
let actions: Vec<_> = (0..100)
.map(|_| (OrchardDomain::for_action(action), action.clone()))
.collect();
let compact: Vec<_> = (0..100)
.map(|_| {
(
OrchardDomain::for_action(action),
CompactAction::from(action),
)
})
.collect();
let mut group = c.benchmark_group("batch-note-decryption");
for size in array::IntoIter::new([10, 50, 100]) {
group.throughput(Throughput::Elements((ivks * size) as u64));
group.bench_function(BenchmarkId::new("valid", size), |b| {
b.iter(|| batch::try_note_decryption(&valid_ivks, &actions[..size]))
});
group.bench_function(BenchmarkId::new("invalid", size), |b| {
b.iter(|| batch::try_note_decryption(&invalid_ivks[..ivks], &actions[..size]))
});
group.bench_function(BenchmarkId::new("compact-valid", size), |b| {
b.iter(|| batch::try_compact_note_decryption(&valid_ivks, &compact[..size]))
});
group.bench_function(BenchmarkId::new("compact-invalid", size), |b| {
b.iter(|| {
batch::try_compact_note_decryption(&invalid_ivks[..ivks], &compact[..size])
})
});
}
}
}
#[cfg(unix)]
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = bench_note_decryption
}
#[cfg(not(unix))]
criterion_group!(benches, bench_note_decryption);
criterion_main!(benches);

View File

@ -1,19 +0,0 @@
use criterion::{criterion_group, criterion_main, Criterion};
use orchard::keys::{FullViewingKey, SpendingKey};
fn key_derivation(c: &mut Criterion) {
// Meaningless random spending key.
let sk = SpendingKey::from_bytes([
0x2e, 0x0f, 0xd6, 0xc0, 0xed, 0x0b, 0xcf, 0xd8, 0x07, 0xf5, 0xdb, 0xff, 0x47, 0x4e, 0xdc,
0x78, 0x8c, 0xe0, 0x09, 0x30, 0x66, 0x10, 0x1e, 0x95, 0x82, 0x87, 0xb1, 0x00, 0x50, 0x9b,
0xf7, 0x9a,
])
.unwrap();
let fvk = FullViewingKey::from(&sk);
c.bench_function("derive_fvk", |b| b.iter(|| FullViewingKey::from(&sk)));
c.bench_function("default_address", |b| b.iter(|| fvk.default_address()));
}
criterion_group!(benches, key_derivation);
criterion_main!(benches);

View File

@ -11,23 +11,11 @@
\BaseLength:{\ell^\mathsf{#1\vphantom{p}}_{\mathsf{base}}}
# Key components
\AuthSignPublic:{\mathsf{ak}}
\NullifierKey:{\mathsf{nk}}
\InViewingKey:{\mathsf{ivk}}
\DiversifiedTransmitBase:{\mathsf{g_d}}
\DiversifiedTransmitBaseRepr:{\mathsf{g\Repr_d}}
\DiversifiedTransmitPublic:{\mathsf{pk_d}}
\DiversifiedTransmitPublicRepr:{\mathsf{pk\Repr_d}}
# Commitments and hashes
\SinsemillaHash:{\mathsf{SinsemillaHash}}
\SinsemillaCommit:{\mathsf{SinsemillaCommit}}
\SinsemillaShortCommit:{\mathsf{SinsemillaShortCommit}}
\CommitIvk:{\mathsf{Commit}^{\InViewingKey}}
\NoteCommit:{\mathsf{NoteCommit}}
# Circuit constraint helper methods

View File

@ -1,20 +1,7 @@
# The Orchard Book
[Orchard](README.md)
- [Concepts](concepts.md)
- [Preliminaries](concepts/preliminaries.md)
- [User Documentation](user.md)
- [Creating keys and addresses](user/keys.md)
- [Creating notes](user/creating-notes.md)
- [Spending notes](user/spending-notes.md)
- [Integration into an existing chain](user/integration.md)
- [Design](design.md)
- [Keys and addresses](design/keys.md)
- [Actions](design/actions.md)
- [Commitments](design/commitments.md)
- [Commitment tree](design/commitment-tree.md)
- [Nullifiers](design/nullifiers.md)
- [Signatures](design/signatures.md)
- [Circuit](design/circuit.md)
- [Gadgets](design/circuit/gadgets.md)
- [Elliptic curve cryptography](design/circuit/gadgets/ecc.md)
@ -23,6 +10,4 @@
- [Variable-base scalar multiplication](design/circuit/gadgets/ecc/var-base-scalar-mul.md)
- [Sinsemilla](design/circuit/gadgets/sinsemilla.md)
- [MerkleCRH](design/circuit/gadgets/sinsemilla/merkle-crh.md)
- [CommitIvk](design/circuit/gadgets/sinsemilla/commit-ivk.md)
- [NoteCommit](design/circuit/gadgets/sinsemilla/note-commit.md)
- [Decomposition](design/circuit/gadgets/decomposition.md)

View File

@ -1 +0,0 @@
# Concepts

View File

@ -1 +0,0 @@
# Preliminaries

View File

@ -1,27 +0,0 @@
# Actions
In Sprout, we had a single proof that represented two spent notes and two new notes. This
was necessary in order to faciliate spending multiple notes in a single transaction (to
balance value, an output of one JoinSplit could be spent in the next one), but also
provided a minimal level of arity-hiding: single-JoinSplit transactions all looked like
2-in 2-out transactions, and in multi-JoinSplit transactions each JoinSplit looked like a
1-in 1-out.
In Sapling, we switched to using value commitments to balance the transaction, removing
the min-2 arity requirement. We opted for one proof per spent note and one (much simpler)
proof per output note, which greatly improved the performance of generating outputs, but
removed any arity-hiding from the proofs (instead having the transaction builder pad
transactions to 1-in, 2-out).
For Orchard, we take a combined approach: we define an Orchard transaction as containing a
bundle of actions, where each action is both a spend and an output. This provides the same
inherent arity-hiding as multi-JoinSplit Sprout, but using Sapling value commitments to
balance the transaction without doubling its size.
TODO: Depending on the circuit cost, we _may_ switch to having an action internally
represent either a spend or an output. Externally spends and outputs would still be
indistinguishable, but the transaction would be larger.
## Memo fields
TODO: One memo per tx vs one memo per output

View File

@ -1,229 +0,0 @@
# $\CommitIvk$
## Message decomposition
$\SinsemillaShortCommit$ is used in the
[$\CommitIvk$ function](https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit).
The input to $\SinsemillaShortCommit$ is:
$$\ItoLEBSP{\BaseLength{Orchard}}(\AuthSignPublic) \bconcat \ItoLEBSP{\BaseLength{Orchard}}(\NullifierKey),$$
where $\AuthSignPublic$, $\NullifierKey$ are Pallas base field elements, and $\BaseLength{Orchard} = 255.$
Sinsemilla operates on multiples of 10 bits, so we start by decomposing the message into
chunks:
$$
\begin{align}
\ItoLEBSP{\BaseLength{Orchard}}(\AuthSignPublic) &= a \bconcat b_0 \bconcat b_1 \\
&= (\text{bits 0..=249 of } \AuthSignPublic) \bconcat
(\text{bits 250..=253 of } \AuthSignPublic) \bconcat
(\text{bit 254 of } \AuthSignPublic) \\
\ItoLEBSP{\BaseLength{Orchard}}(\NullifierKey) &= b_2 \bconcat c \bconcat d_0 \bconcat d_1 \\
&= (\text{bits 0..=4 of } \NullifierKey) \bconcat
(\text{bits 5..=244 of } \NullifierKey) \bconcat
(\text{bits 245..=253 of } \NullifierKey) \bconcat
(\text{bit 254 of } \NullifierKey) \\
\end{align}
$$
Then we recompose the chunks into message pieces:
$$
\begin{array}{|c|l|}
\hline
\text{Length (bits)} & \text{Piece} \\\hline
250 & a \\
10 & b = b_0 \bconcat b_1 \bconcat b_2 \\
240 & c \\
10 & d = d_0 \bconcat d_1 \\\hline
\end{array}
$$
Each message piece is constrained by $\SinsemillaHash$ to its stated length. Additionally,
$\AuthSignPublic$ and $\NullifierKey$ are witnessed as field elements, so we know they are
canonical. However, we need additional constraints to enforce that:
- The chunks are the correct bit lengths (or else they could overlap in the decompositions
and allow the prover to witness an arbitrary $\SinsemillaShortCommit$ message).
- The chunks contain the canonical decompositions of $\AuthSignPublic$ and $\NullifierKey$
(or else the prover could witness an input to $\SinsemillaShortCommit$ that is
equivalent to $\AuthSignPublic$ and $\NullifierKey$ but not identical).
Some of these constraints can be implemented with reusable circuit gadgets. We define a
custom gate controlled by the selector $q_\CommitIvk$ to hold the remaining constraints.
## Bit length constraints
Chunks $a$ and $c$ are directly constrained by Sinsemilla. For the remaining chunks, we
use the following constraints:
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
& \ShortLookupRangeCheck{b_0, 4} \\\hline
& \ShortLookupRangeCheck{b_2, 5} \\\hline
& \ShortLookupRangeCheck{d_0, 9} \\\hline
3 & q_\CommitIvk \cdot \BoolCheck{b_1} = 0 \\\hline
3 & q_\CommitIvk \cdot \BoolCheck{d_1} = 0 \\\hline
\end{array}
$$
where $\BoolCheck{x} = x \cdot (1 - x)$ and $\ShortLookupRangeCheck{}$ is a
[short lookup range check](../decomposition.md#short-range-check).
## Decomposition constraints
We have now derived or witnessed every subpiece, and range-constrained every subpiece:
- $a$ ($250$ bits) is witnessed and constrained outside the gate;
- $b_0$ ($4$ bits) is witnessed and constrained outside the gate;
- $b_1$ ($1$ bits) is witnessed and boolean-constrained in the gate;
- $b_2$ ($5$ bits) is witnessed and constrained outside the gate;
- $c$ ($240$ bits) is witnessed and constrained outside the gate;
- $d_0$ ($9$ bits) is witnessed and constrained outside the gate;
- $d_1$ ($1$ bits) is witnessed and boolean-constrained in the gate.
We can now use them to reconstruct both the (chunked) message pieces, and the original
field element inputs:
$$
\begin{align}
b &= b_0 + 2^4 \cdot b_1 + 2^5 \cdot b_2 \\
d &= d_0 + 2^9 \cdot d_1 \\
\AuthSignPublic &= a + 2^{250} \cdot b_0 + 2^{254} \cdot b_1 \\
\NullifierKey &= b_2 + 2^5 \cdot c + 2^{245} \cdot d_0 + 2^{254} \cdot d_1 \\
\end{align}
$$
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_\CommitIvk \cdot (b - (b_0 + b_1 \cdot 2^4 + b_2 \cdot 2^5)) = 0 \\\hline
2 & q_\CommitIvk \cdot (d - (d_0 + d_1 \cdot 2^9)) = 0 \\\hline
2 & q_\CommitIvk \cdot (a + b_0 \cdot 2^{250} + b_1 \cdot 2^{254} - \AuthSignPublic) = 0 \\\hline
2 & q_\CommitIvk \cdot (b_2 + c \cdot 2^5 + d_0 \cdot 2^{245} + d_1 \cdot 2^{254} - \NullifierKey) = 0 \\\hline
\end{array}
$$
## Canonicity checks
At this point, we have constrained $\ItoLEBSP{\BaseLength{Orchard}}(\AuthSignPublic)$ and
$\ItoLEBSP{\BaseLength{Orchard}}(\NullifierKey)$ to be 255-bit values, with top bits $b_1$
and $d_1$ respectively. We have also constrained:
$$
\begin{align}
\ItoLEBSP{\BaseLength{Orchard}}(\AuthSignPublic) &= \AuthSignPublic \pmod{q_\mathbb{P}} \\
\ItoLEBSP{\BaseLength{Orchard}}(\NullifierKey) &= \NullifierKey \pmod{q_\mathbb{P}} \\
\end{align}
$$
where $q_\mathbb{P}$ is the Pallas base field modulus. The remaining constraints will
enforce that these are indeed canonically-encoded field elements, i.e.
$$
\begin{align}
\ItoLEBSP{\BaseLength{Orchard}}(\AuthSignPublic) &< q_\mathbb{P} \\
\ItoLEBSP{\BaseLength{Orchard}}(\NullifierKey) &< q_\mathbb{P} \\
\end{align}
$$
The Pallas base field modulus has the form $q_\mathbb{P} = 2^{254} + t_\mathbb{P}$, where
$$t_\mathbb{P} = \mathtt{0x224698fc094cf91b992d30ed00000001}$$
is 126 bits. We therefore know that if the top bit is not set, then the remaining bits
will always comprise a canonical encoding of a field element. Thus the canonicity checks
below are enforced if and only if $b_1 = 1$ (for $\AuthSignPublic$) or $d_1 = 1$ (for
$\NullifierKey$).
> In the constraints below we use a base-$2^{10}$ variant of the method used in libsnark
> (originally from [[SVPBABW2012](https://eprint.iacr.org/2012/598.pdf), Appendix C.1]) for
> range constraints $0 \leq x < t$:
>
> - Let $t'$ be the smallest power of $2^{10}$ greater than $t$.
> - Enforce $0 \leq x < t'$.
> - Let $x' = x + t' - t$.
> - Enforce $0 \leq x' < t'$.
### $\AuthSignPublic$ with $b_1 = 1 \implies \AuthSignPublic \geq 2^{254}$
In these cases, we check that $\textsf{ak}_{0..=253} < t_\mathbb{P}$:
1. $b_1 = 1 \implies b_0 = 0.$
Since $b_1 = 1 \implies \AuthSignPublic_{0..=253} < t_\mathbb{P} < 2^{126},$ we know that
$\AuthSignPublic_{126..=253} = 0,$ and in particular
$$b_0 := \AuthSignPublic_{250..=253} = 0.$$
2. $b_1 = 1 \implies 0 \leq a < t_\mathbb{P}.$
To check that $a < t_\mathbb{P}$, we use two constraints:
a) $0 \leq a < 2^{130}$. This is expressed in the custom gate as
$$b_1 \cdot z_{a,13} = 0,$$
where $z_{a,13}$ is the index-13 running sum output by $\SinsemillaHash(a).$
b) $0 \leq a + 2^{130} - t_\mathbb{P} < 2^{130}$. To check this, we decompose
$a' = a + 2^{130} - t_\mathbb{P}$ into thirteen 10-bit words (little-endian) using
a running sum $z_{a'}$, looking up each word in a $10$-bit lookup table. We then
enforce in the custom gate that
$$b_1 \cdot z_{a',13} = 0.$$
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_\CommitIvk \cdot b_1 \cdot b_0 = 0 \\\hline
3 & q_\CommitIvk \cdot b_1 \cdot z_{a,13} = 0 \\\hline
2 & q_\CommitIvk \cdot (a + 2^{130} - t_\mathbb{P} - a') = 0 \\\hline
3 & q_\CommitIvk \cdot b_1 \cdot z_{a',13} = 0 \\\hline
\end{array}
$$
### $\NullifierKey$ with $d_1 = 1 \implies \NullifierKey \geq 2^{254}$
In these cases, we check that $\textsf{nk}_{0..=253} < t_\mathbb{P}$:
1. $d_1 = 1 \implies d_0 = 0.$
Since $d_1 = 1 \implies \NullifierKey_{0..=253} < t_\mathbb{P} < 2^{126},$ we know that $\NullifierKey_{126..=253} = 0,$ and in particular $$d_0 := \NullifierKey_{245..=253} = 0.$$
2. $d_1 = 1 \implies 0 \leq b_2 + 2^5 \cdot c < t_\mathbb{P}.$
To check that $0 \leq b_2 + 2^5 \cdot c < t_\mathbb{P}$, we use two constraints:
a) $0 \leq b_2 + 2^5 \cdot c < 2^{140}$. $b_2$ is already constrained individually to
be a $5$-bit value. $z_{c,13}$ is the index-13 running sum output by
$\SinsemillaHash(c).$ By constraining $$d_1 \cdot z_{c,13} = 0,$$ we constrain
$b_2 + 2^5 \cdot c < 2^{135} < 2^{140}.$
b) $0 \leq b_2 + 2^5 \cdot c + 2^{140} - t_\mathbb{P} < 2^{140}$. To check this, we
decompose ${b_2}c' = b_2 + 2^5 \cdot c + 2^{140} - t_\mathbb{P}$ into fourteen
10-bit words (little-endian) using a running sum $z_{{b_2}c'}$, looking up each
word in a $10$-bit lookup table. We then enforce in the custom gate that
$$d_1 \cdot z_{{b_2}c',14} = 0.$$
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_\CommitIvk \cdot d_1 \cdot d_0 = 0 \\\hline
3 & q_\CommitIvk \cdot d_1 \cdot z_{c,13} = 0 \\\hline
2 & q_\CommitIvk \cdot (b_2 + c \cdot 2^5 + 2^{140} - t_\mathbb{P} - {b_2}c') = 0 \\\hline
3 & q_\CommitIvk \cdot d_1 \cdot z_{{b_2}c',14} = 0 \\\hline
\end{array}
$$
## Region layout
The constraints controlled by the $q_\CommitIvk$ selector are arranged across 9
advice columns, requiring two rows.
$$
\begin{array}{|c|c|c|c|c|c|c|c|c|c}
& & & & & & & & & q_\CommitIvk \\\hline
\AuthSignPublic & a & b & b_0 & b_1 & b_2 & z_{a,13} & a' & z_{a',13} & 1 \\\hline
\NullifierKey & c & d & d_0 & d_1 & & z_{c,13} & {b_2}c' & z_{{b_2}c',14} & 0 \\\hline
\end{array}
$$

View File

@ -1,579 +0,0 @@
# NoteCommit
## Message decomposition
$\SinsemillaCommit$ is used in the
[$\NoteCommit$ function](https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit).
The input to $\SinsemillaCommit$ is:
$$\DiversifiedTransmitBaseRepr \bconcat
\DiversifiedTransmitPublicRepr \bconcat
\ItoLEBSP{64}(\mathsf{v}) \bconcat
\ItoLEBSP{\BaseLength{Orchard}}(\rho) \bconcat
\ItoLEBSP{\BaseLength{Orchard}}(\psi),$$
where:
- $\DiversifiedTransmitBaseRepr, \DiversifiedTransmitPublicRepr$ are representations of
Pallas curve points, with $255$ bits used for the $x$-coordinate and $1$ bit used for
the $y$-coordinate.
- $\rho, \psi$ are Pallas base field elements.
- $\mathsf{v}$ is a $64$-bit value.
- $\BaseLength{Orchard} = 255.$
Sinsemilla operates on multiples of 10 bits, so we start by decomposing the message into
chunks:
$$
\begin{aligned}
\DiversifiedTransmitBaseRepr &= a \bconcat b_0 \bconcat b_1 \bconcat b_2 \\
&= (\text{bits 0..=249 of } x(\mathsf{g_d})) \bconcat
(\text{bits 250..=253 of } x(\mathsf{g_d})) \bconcat
(\text{bit 254 of } x(\mathsf{g_d})) \bconcat
(ỹ \text{ bit of } \mathsf{g_d}) \\
\DiversifiedTransmitPublicRepr &= b_3 \bconcat c \bconcat d_0 \bconcat d_1 \\
&= (\text{bits 0..=3 of } x(\mathsf{pk_d})) \bconcat
(\text{bits 4..=253 of } x(\mathsf{pk_d})) \bconcat
(\text{bit 254 of } x(\mathsf{pk_d})) \bconcat
(ỹ \text{ bit of } \mathsf{pk_d}) \\
\ItoLEBSP{64}(v) &= d_2 \bconcat d_3 \bconcat e_0 \\
&= (\text{bits 0..=7 of } v) \bconcat
(\text{bits 8..=57 of } v) \bconcat
(\text{bits 58..=63 of } v) \\
\ItoLEBSP{\BaseLength{Orchard}}(\rho) &= e_1 \bconcat f \bconcat g_0 \\
&= (\text{bits 0..=3 of } \rho) \bconcat
(\text{bits 4..=253 of } \rho) \bconcat
(\text{bit 254 of } \rho) \\
\ItoLEBSP{\BaseLength{Orchard}}(\psi) &= g_1 \bconcat g_2 \bconcat h_0 \bconcat h_1 \\
&= (\text{bits 0..=8 of } \psi) \bconcat
(\text{bits 9..=248 of } \psi) \bconcat
(\text{bits 249..=253 of } \psi) \bconcat
(\text{bit 254 of } \psi) \\
\end{aligned}
$$
Then we recompose the chunks into message pieces:
$$
\begin{array}{|c|l|}
\hline
\text{Length (bits)} & \text{Piece} \\\hline
250 & a \\
10 & b = b_0 \bconcat b_1 \bconcat b_2 \bconcat b_3 \\
250 & c \\
60 & d = d_0 \bconcat d_1 \bconcat d_2 \bconcat d_3 \\
10 & e = e_0 \bconcat e_1 \\
250 & f \\
250 & g = g_0 \bconcat g_1 \bconcat g_2 \\
10 & h = h_0 \bconcat h_1 \bconcat h_2 \\\hline
\end{array}
$$
where $h_2$ is 4 zero bits (corresponding to the padding applied by the Sinsemilla
[$\mathsf{pad}$ function](https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash)).
Each message piece is constrained by $\SinsemillaHash$ to its stated length. Additionally:
- $\DiversifiedTransmitBase$ and $\DiversifiedTransmitPublic$ are witnessed and checked
to be valid elliptic curve points.
- $\mathsf{v}$ is witnessed as a field element, but its decomposition is sufficient to
constrain it to be a 64-bit value.
- $\rho$ and $\psi$ are witnessed as field elements, so we know they are canonical.
However, we need additional constraints to enforce that:
- The chunks are the correct bit lengths (or else they could overlap in the decompositions
and allow the prover to witness an arbitrary $\SinsemillaCommit$ message).
- The chunks contain the canonical decompositions of $\DiversifiedTransmitBase$,
$\DiversifiedTransmitPublic$, $\rho$, and $\psi$ (or else the prover could witness
multiple equivalent inputs to $\SinsemillaCommit$).
Some of these constraints are implemented with a reusable circuit gadget,
$\ShortLookupRangeCheck{}$. We define custom gates for the remainder. Since these gates
use simple boolean selectors activated on different rows, their selectors are eligible
for combining, reducing the overall proof size.
## Message piece decomposition
We check the decomposition of each message piece in its own region. There is no need to
check the whole pieces:
- $a$ ($250$ bits) is witnessed and constrained outside the gate;
- $c$ ($250$ bits) is witnessed and constrained outside the gate;
- $f$ ($250$ bits) is witnessed and constrained outside the gate;
The following helper gates are defined:
- $\BoolCheck{x} = x \cdot (1 - x)$.
- $\ShortLookupRangeCheck{}$ is a
[short lookup range check](../decomposition.md#short-range-check).
### $b = b_0 \bconcat b_1 \bconcat b_2 \bconcat b_3$
$b$ has been constrained to be $10$ bits by the Sinsemilla hash.
#### Region layout
$$
\begin{array}{|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & q_{\NoteCommit,b} \\\hline
b & b_0 & b_1 & 1 \\\hline
& b_2 & b_3 & 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,b} \cdot \BoolCheck{b_1} = 0 \\\hline
3 & q_{\NoteCommit,b} \cdot \BoolCheck{b_2} = 0 \\\hline
2 & q_{\NoteCommit,b} \cdot (b - (b_0 + b_1 \cdot 2^4 + b_2 \cdot 2^5 + b_3 \cdot 2^6)) = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{b_0, 4}$
- $\ShortLookupRangeCheck{b_3, 4}$
### $d = d_0 \bconcat d_1 \bconcat d_2 \bconcat d_3$
$d$ has been constrained to be $60$ bits by the $\SinsemillaHash$.
#### Region layout
$$
\begin{array}{|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & q_{\NoteCommit,d} \\\hline
d & d_0 & d_1 & 1 \\\hline
& d_2 & d_3 & 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,d} \cdot \BoolCheck{d_0} = 0 \\\hline
3 & q_{\NoteCommit,d} \cdot \BoolCheck{d_1} = 0 \\\hline
2 & q_{\NoteCommit,d} \cdot (d - (d_0 + d_1 \cdot 2 + d_2 \cdot 2^2 + d_3 \cdot 2^{10})) = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{d_2, 8}$
- $d_3$ is equality-constrained to $z_{d,1}$, where the latter is the index-1 running sum
output of $\SinsemillaHash(d),$ constrained by the hash to be $50$ bits.
### $e = e_0 \bconcat e_1$
$e$ has been constrained to be $10$ bits by the $\SinsemillaHash$.
#### Region layout
$$
\begin{array}{|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & q_{\NoteCommit,e} \\\hline
e & e_0 & e_1 & 1 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,e} \cdot (e - (e_0 + e_1 \cdot 2^6)) = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{e_0, 6}$
- $\ShortLookupRangeCheck{e_1, 4}$
### $g = g_0 \bconcat g_1 \bconcat g_2$
$g$ has been constrained to be $250$ bits by the $\SinsemillaHash$.
#### Region layout
$$
\begin{array}{|c|c|c|c|}
\hline
A_6 & A_7 & q_{\NoteCommit,g} \\\hline
g & g_0 & 1 \\\hline
g_1 & g_2 & 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,g} \cdot \BoolCheck{g_0} = 0 \\\hline
2 & q_{\NoteCommit,g} \cdot (g - (g_0 + g_1 \cdot 2 + g_2 \cdot 2^{10})) = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{g_1, 9}$
- $g_2$ is equality-constrained to $z_{g,1}$, where the latter is the index-1 running sum
output of $\SinsemillaHash(g),$ constrained by the hash to be 240 bits.
### $h = h_0 \bconcat h_1 \bconcat h_2$
$h$ has been constrained to be $10$ bits by the $\SinsemillaHash$.
#### Region layout
$$
\begin{array}{|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & q_{\NoteCommit,h} \\\hline
h & h_0 & h_1 & 1 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,h} \cdot \BoolCheck{h_1} = 0 \\\hline
2 & q_{\NoteCommit,h} \cdot (h - (h_0 + h_1 \cdot 2^5)) = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{h_0, 5}$
## Field element checks
All message pieces and subpieces have been range-constrained by the earlier decomposition
gates. They are now used to:
- constrain each field element $\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{g_d}))$,
$\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{pk_d}))$,
$\ItoLEBSP{\BaseLength{Orchard}}(\rho)$, and $\ItoLEBSP{\BaseLength{Orchard}}(\psi)$ to
be 255-bit values, with top bits $b_1$, $d_0$, $g_0$, and $h_1$ respectively.
- constrain $$
\begin{align}
\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{g_d})) &= x(\mathsf{g_d}) \pmod{q_\mathbb{P}} \\
\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{pk_d})) &= x(\mathsf{pk_d}) \pmod{q_\mathbb{P}} \\
\ItoLEBSP{\BaseLength{Orchard}}(\rho) &= \rho \pmod{q_\mathbb{P}} \\
\ItoLEBSP{\BaseLength{Orchard}}(\psi) &= \psi \pmod{q_\mathbb{P}} \\
\end{align}
$$
where $q_\mathbb{P}$ is the Pallas base field modulus.
- check that these are indeed canonically-encoded field elements, i.e. $$
\begin{align}
\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{g_d})) &< q_\mathbb{P} \\
\ItoLEBSP{\BaseLength{Orchard}}(x(\mathsf{pk_d})) &< q_\mathbb{P} \\
\ItoLEBSP{\BaseLength{Orchard}}(\rho) &< q_\mathbb{P} \\
\ItoLEBSP{\BaseLength{Orchard}}(\psi) &< q_\mathbb{P} \\
\end{align}
$$
The Pallas base field modulus has the form $q_\mathbb{P} = 2^{254} + t_\mathbb{P}$, where
$$t_\mathbb{P} = \mathtt{0x224698fc094cf91b992d30ed00000001}$$
is 126 bits. We therefore know that if the top bit is not set, then the remaining bits
will always comprise a canonical encoding of a field element. Thus the canonicity checks
below are enforced if and only if the corresponding top bit is set to 1.
> In the constraints below we use a base-$2^{10}$ variant of the method used in libsnark
> (originally from [[SVPBABW2012](https://eprint.iacr.org/2012/598.pdf), Appendix C.1]) for
> range constraints $0 \leq x < t$:
>
> - Let $t'$ be the smallest power of $2^{10}$ greater than $t$.
> - Enforce $0 \leq x < t'$.
> - Let $x' = x + t' - t$.
> - Enforce $0 \leq x' < t'$.
### $x(\mathsf{g_d})$ with $b_1 = 1 \implies x(\mathsf{g_d}) \geq 2^{254}$
Recall that $x(\mathsf{g_d}) = a + 2^{250} \cdot b_0 + 2^{254} \cdot b_1$. When the top
bit $b_1$ is set, we check that $x(\mathsf{g_d})_{0..=253} < t_\mathbb{P}$:
1. $b_1 = 1 \implies b_0 = 0.$
Since $b_1 = 1 \implies x(\mathsf{g_d})_{0..=253} < t_\mathbb{P} < 2^{126},$ we know
that $x(\mathsf{g_d})_{126..=253} = 0,$ and in particular
$$b_0 := x(\mathsf{g_d})_{250..=253} = 0.$$
2. $b_1 = 1 \implies 0 \leq a < t_\mathbb{P}.$
To check that $a < t_\mathbb{P}$, we use two constraints:
a) $0 \leq a < 2^{130}$. This is expressed in the custom gate as
$$b_1 \cdot z_{a,13} = 0,$$
where $z_{a,13}$ is the index-13 running sum output by $\SinsemillaHash(a).$
b) $0 \leq a + 2^{130} - t_\mathbb{P} < 2^{130}$. To check this, we decompose
$a' = a + 2^{130} - t_\mathbb{P}$ into thirteen 10-bit words (little-endian) using
a running sum $z_{a'}$, looking up each word in a $10$-bit lookup table. We then
enforce in the custom gate that
$$b_1 \cdot z_{a',13} = 0.$$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,x(\mathsf{g_d})} \\\hline
x(\mathsf{g_d}) & b_0 & a & z_{a,13} & 1 \\\hline
& b_1 & a' & z_{a',13}& 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,x(\mathsf{g_d})} \cdot (a + b_0 \cdot 2^{250} + b_1 \cdot 2^{254} - x(\mathsf{g_d})) = 0 \\\hline
3 & q_{\NoteCommit,x(\mathsf{g_d})} \cdot b_1 \cdot b_0 = 0 \\\hline
3 & q_{\NoteCommit,x(\mathsf{g_d})} \cdot b_1 \cdot z_{a,13} = 0 \\\hline
2 & q_{\NoteCommit,x(\mathsf{g_d})} \cdot (a + 2^{130} - t_\mathbb{P} - a') = 0 \\\hline
3 & q_{\NoteCommit,x(\mathsf{g_d})} \cdot b_1 \cdot z_{a',13} = 0 \\\hline
\end{array}
$$
### $x(\mathsf{pk_d})$ with $d_0 = 1 \implies x(\mathsf{pk_d}) \geq 2^{254}$
Recall that $x(\mathsf{pk_d}) = b_3 + 2^4 \cdot c + 2^{254} \cdot d_0$. When the top bit
$d_0$ is set, we check that $x(\mathsf{pk_d})_{0..=253} < t_\mathbb{P}$:
1. $d_0 = 1 \implies 0 \leq b_3 + 2^{4} \cdot c < t_\mathbb{P}.$
To check that $0 \leq b_3 + 2^{4} \cdot c < t_\mathbb{P},$ we use two constraints:
a) $0 \leq b_3 + 2^{4} \cdot c < 2^{140}.$ $b_3$ is already constrained individually
to be a $4$-bit value. $z_{c,13}$ is the index-13 running sum output by
$\SinsemillaHash(c).$ By constraining $$d_0 \cdot z_{c,13} = 0,$$ we constrain
$b_3 + 2^4 \cdot c < 2^{134} < 2^{140}.$
b) $0 \leq b_3 + 2^{4} \cdot c + 2^{140} - t_\mathbb{P} < 2^{140}$. To check this, we
decompose ${b_3}c' = b_3 + 2^{4} \cdot c + 2^{140} - t_\mathbb{P}$ into fourteen
10-bit words (little-endian) using a running sum $z_{{b_3}c'}$, looking up each
word in a $10$-bit lookup table. We then enforce in the custom gate that
$$d_0 \cdot z_{{b_3}c',14} = 0.$$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,x(\mathsf{pk_d})} \\\hline
x(\mathsf{pk_d}) & b_3 & c & z_{c,13} & 1 \\\hline
& d_0 & b_3c' & z_{b_3c',14} & 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,x(\mathsf{pk_d})} \cdot (b_3 + c \cdot 2^4 + d_0 \cdot 2^{254} - x(\mathsf{pk_d}) = 0 \\\hline
3 & q_{\NoteCommit,x(\mathsf{pk_d})} \cdot d_0 \cdot z_{c,13} = 0 \\\hline
2 & q_{\NoteCommit,x(\mathsf{pk_d})} \cdot (b_3 + c \cdot 2^4 + 2^{140} - t_\mathbb{P} - {b_3}c') = 0 \\\hline
3 & q_{\NoteCommit,x(\mathsf{pk_d})} \cdot d_0 \cdot z_{{b_3}c',14} = 0 \\\hline
\end{array}
$$
### $\mathsf{v} = d_2 + 2^8 \cdot d_3 + 2^{58} \cdot e_0$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,value} \\\hline
value & d_2 & d_3 & e_0 & 1 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,value} \cdot (d_2 + d_3 \cdot 2^8 + e_0 \cdot 2^{58} - \mathsf{value}) = 0 \\\hline
\end{array}
$$
### $\rho$ with $g_0 = 1 \implies \rho \geq 2^{254}$
Recall that $\rho = e_1 + 2^4 \cdot f + 2^{254} \cdot g_0$. When the top bit $g_0$ is set,
we check that $\rho_{0..=253} < t_\mathbb{P}$:
1. $g_0 = 1 \implies 0 \leq e_1 + 2^{4} \cdot f < t_\mathbb{P}.$
To check that $0 \leq e_1 + 2^{4} \cdot f < t_\mathbb{P},$ we use two constraints:
a) $0 \leq e_1 + 2^{4} \cdot f < 2^{140}.$ $e_1$ is already constrained individually
to be a $4$-bit value. $z_{f,13}$ is the index-13 running sum output by
$\SinsemillaHash(f).$ By constraining $$g_0 \cdot z_{f,13} = 0,$$ we constrain
$e_1 + 2^4 \cdot f < 2^{134} < 2^{140}.$
b) $0 \leq e_1 + 2^{4} \cdot f + 2^{140} - t_\mathbb{P} < 2^{140}$. To check this, we
decompose ${e_1}f' = e_1 + 2^{4} \cdot f + 2^{140} - t_\mathbb{P}$ into fourteen
10-bit words (little-endian) using a running sum $z_{{e_1}f'}$, looking up each
word in a $10$-bit lookup table. We then enforce in the custom gate that
$$g_0 \cdot z_{{e_1}f',14} = 0.$$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,\rho} \\\hline
\rho & e_1 & f & z_{f,13} & 1 \\\hline
& g_0 & e_1f' & z_{e_1f',14}& 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,\rho} \cdot (e_1 + f \cdot 2^4 + g_0 \cdot 2^{254} - \rho) = 0 \\\hline
3 & q_{\NoteCommit,\rho} \cdot g_0 \cdot z_{f,13} = 0 \\\hline
2 & q_{\NoteCommit,\rho} \cdot (e_1 + f \cdot 2^4 + 2^{140} - t_\mathbb{P} - {e_1}f') = 0 \\\hline
3 & q_{\NoteCommit,\rho} \cdot g_0 \cdot z_{{e_1}f',14} = 0 \\\hline
\end{array}
$$
### $\psi$ with $h_1 = 1 \implies \psi \geq 2^{254}$
Recall that $\psi = g_1 + 2^9 \cdot g_2 + 2^{249} \cdot h_0 + 2^{254} \cdot h_1$. When the
top bit $h_1$ is set, we check that $\psi_{0..=253} < t_\mathbb{P}$:
1. $h_1 = 1 \implies h_0 = 0.$
Since $h_1 = 1 \implies \psi_{0..=253} < t_\mathbb{P} < 2^{126},$ we know that
$\psi_{126..=253} = 0,$ and in particular $h_0 := \psi_{249..=253} = 0.$
2. $h_1 = 1 \implies 0 \leq g_1 + 2^{9} \cdot g_2 < t_\mathbb{P}.$
To check that $0 \leq g_1 + 2^{9} \cdot g_2 < t_\mathbb{P},$ we use two constraints:
a) $0 \leq g_1 + 2^{9} \cdot g_2 < 2^{140}.$ $g_1$ is already constrained individually
to be a $9$-bit value. $z_{g,13}$ is the index-13 running sum output by
$\SinsemillaHash(g).$ By constraining $$h_1 \cdot z_{g,13} = 0,$$ we constrain
$g_1 + 2^9 \cdot g_2 < 2^{129} < 2^{130}.$
b) $0 \leq g_1 + 2^{9} \cdot g_2 + 2^{130} - t_\mathbb{P} < 2^{130}$. To check this,
we decompose ${g_1}{g_2}' = g_1 + 2^{9} \cdot g_2 + 2^{130} - t_\mathbb{P}$ into
thirteen 10-bit words (little-endian) using a running sum $z_{{g_1}{g_2}'}$,
looking up each word in a $10$-bit lookup table. We then enforce in the custom gate
that $$h_1 \cdot z_{{g_1}{g_2}',13} = 0.$$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|}
\hline
A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,\psi} \\\hline
\psi & g_1 & g_2 & z_{g,13} & 1 \\\hline
h_0 & h_1 & g_1g_2' & z_{g_1g_2',13}& 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,\psi} \cdot (g_1 + g_2 \cdot 2^9 + h_0 \cdot 2^{249} + h_1 \cdot 2^{254} - \psi) = 0 \\\hline
3 & q_{\NoteCommit,\psi} \cdot h_1 \cdot h_0 = 0 \\\hline
3 & q_{\NoteCommit,\psi} \cdot h_1 \cdot z_{g,13} = 0 \\\hline
2 & q_{\NoteCommit,\psi} \cdot (g_1 + g_2 \cdot 2^9 + 2^{130} - t_\mathbb{P} - {g_1}{g_2}') = 0 \\\hline
3 & q_{\NoteCommit,\psi} \cdot h_1 \cdot z_{{g_1}{g_2}',13} = 0 \\\hline
\end{array}
$$
### $y$-coordinate checks
Note that only the $ỹ$ LSB of the $y$-coordinates $y(\mathsf{g_d}), y(\mathsf{pk_d})$ was
input to the hash, while the other bits of the $y$-coordinate were unused. However, we
must still check that the witnessed $ỹ$ bit matches the original point's $y$-coordinate.
The checks for $y(\mathsf{g_d}), y(\mathsf{pk_d})$ will follow the same format. For each
$y$-coordinate, we witness:
$$
\begin{align}
y &= \textsf{LSB} \bconcat k_0 \bconcat k_1 \bconcat k_2 \bconcat k_3\\
&= \textsf{LSB}
\bconcat \text{ (bits $1..=9$ of $y$) }
\bconcat \text{ (bits $10..=249$ of $y$) }
\bconcat \text{ (bits $250..=253$ of $y$) }
\bconcat \text{ (bit $254$ of $y$) },
\end{align}
$$
where $\textsf{LSB}$ is $b_2$ for $y(\mathsf{g_d})$, and $d_1$ for $y(\mathsf{pk_d})$.
Let $$j = \textsf{LSB} + 2 \cdot k_0 + 2^{10} \cdot k_1.$$ We decompose $j$ to be $250$
bits using a strict $25-$word [ten-bit lookup](../decomposition.md#lookup-decomposition).
The running sum outputs allow us to susbstitute $k_1 = z_{j, 1}.$
Recall that $b_2 = ỹ(\mathsf{g_d})$ and $d_1 = ỹ(\mathsf{pk_d})$ were pieces input to the
Sinsemilla hash and have already been boolean-constrained. $k_0$ and $k_2$ are constrained
outside this gate to $9$ and $4$ bits respectively. To constrain the remaining chunks, we
use the following constraints:
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,y} \cdot \BoolCheck{k_3} = 0 \\\hline
\end{array}
$$
Then, to check that the decomposition was correct:
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
2 & q_{\NoteCommit,y} \cdot \left(j - (\textsf{LSB} + k_0 \cdot 2 + k_1 \cdot 2^{10}) \right) = 0 \\\hline
2 & q_{\NoteCommit,y} \cdot \left(y - (j + k_2 \cdot 2^{250} + k_3 \cdot 2^{254}) \right) = 0 \\\hline
\end{array}
$$
### $y(\mathsf{g_d})$ with $k_3 = 1 \implies y(\mathsf{g_d}) \geq 2^{254}$
In these cases, we check that $y(\mathsf{g_d})_{0..=253} < t_\mathbb{P}$:
1. $k_3 = 1 \implies k_2 = 0.$
Since $k_3 = 1 \implies y(\mathsf{g_d})_{0..=253} < t_\mathbb{P} < 2^{126},$ we know that
$y(\mathsf{g_d})_{126..=253} = 0,$ and in particular
$$k_2 := y(\mathsf{g_d})_{250..=253} = 0.$$
2. $k_3 = 1 \implies 0 \leq j < t_\mathbb{P}.$
To check that $j < t_\mathbb{P}$, we use two constraints:
a) $0 \leq j < 2^{130}$. This is expressed in the custom gate as
$$k_3 \cdot z_{j,13} = 0,$$
where $z_{j,13}$ is the index-13 running sum output by the $10$-bit lookup
decomposition of $j$.
b) $0 \leq j + 2^{130} - t_\mathbb{P} < 2^{130}$. To check this, we decompose
$j' = j + 2^{130} - t_\mathbb{P}$ into thirteen 10-bit words (little-endian) using
a running sum $z_{j'}$, looking up each word in a $10$-bit lookup table. We then
enforce in the custom gate that
$$k_3 \cdot z_{j',13} = 0.$$
#### Region layout
$$
\begin{array}{|c|c|c|c|c|c|}
\hline
A_5 & A_6 & A_7 & A_8 & A_9 & q_{\NoteCommit,y} \\\hline
y && k_0 & k_2 & k_3 & 1 \\\hline
j & k_1 & z_{j,13} & j' & z_{j',13} & 0 \\\hline
\end{array}
$$
#### Constraints
$$
\begin{array}{|c|l|}
\hline
\text{Degree} & \text{Constraint} \\\hline
3 & q_{\NoteCommit,y} \cdot k_3 \cdot k_2 = 0 \\\hline
3 & q_{\NoteCommit,y} \cdot k_3 \cdot z_{j,13} = 0 \\\hline
2 & q_{\NoteCommit,y} \cdot (j + 2^{130} - t_\mathbb{P} - j') = 0 \\\hline
3 & q_{\NoteCommit,y} \cdot k_3 \cdot z_{j',13} = 0 \\\hline
\end{array}
$$
Outside this gate, we have constrained:
- $\ShortLookupRangeCheck{k_0, 9}$
- $\ShortLookupRangeCheck{k_2, 4}$
### $y(\mathsf{pk_d})$
This can be checked in exactly the same way as $y(\mathsf{g_d})$, with $b_2$ replaced by
$d_1$.

View File

@ -1,75 +0,0 @@
# Commitment tree
The commitment tree structure for Orchard is identical to Sapling:
- A single global commitment tree of fixed depth 32.
- Note commitments are appended to the tree in-order from the block.
- Valid Orchard anchors correspond to the global tree state at block boundaries (after all
commitments from a block have been appended, and before any commitments from the next
block have been appended).
The only difference is that we instantiate $\mathsf{MerkleCRH}^\mathsf{Orchard}$ with
Sinsemilla (whereas $\mathsf{MerkleCRH}^\mathsf{Sapling}$ used a Bowe--Hopwood Pedersen
hash).
## Uncommitted leaves
The fixed-depth incremental Merkle trees that we use (in Sprout and Sapling, and again in
Orchard) require specifying an "empty" or "uncommitted" leaf - a value that will never be
appended to the tree as a regular leaf.
- For Sprout (and trees composed of the outputs of bit-twiddling hash functions), we use
the all-zeroes array; the probability of a real note having a colliding note commitment
is cryptographically negligible.
- For Sapling, where leaves are $u$-coordinates of Jubjub points, we use the value $1$
which is not the $u$-coordinate of any Jubjub point.
Orchard note commitments are the $x$-coordinates of Pallas points; thus we take the same
approach as Sapling, using a value that is not the $x$-coordinate of any Pallas point as the
uncommitted leaf value. We use the value $2$ for both Pallas and Vesta, because $2^3 + 5$ is
not a square in either $F_p$ or $F_q$:
```python
sage: p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
sage: q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
sage: EllipticCurve(GF(p), [0, 5]).count_points() == q
True
sage: EllipticCurve(GF(q), [0, 5]).count_points() == p
True
sage: Mod(13, p).is_square()
False
sage: Mod(13, q).is_square()
False
```
> Note: There are also no Pallas points with $x$-coordinate $0$, but we map the identity to
> $(0, 0)$ within the circuit. Although $\mathsf{SinsemillaCommit}$ cannot return the identity
> (the incomplete addition would return $\perp$ instead), it would arguably be confusing to
> rely on that.
## Considered alternatives
We considered splitting the commitment tree into several sub-trees:
- Bundle tree, that accumulates the commitments within a single bundle (and thus a single
transaction).
- Block tree, that accumulates the bundle tree roots within a single block.
- Global tree, that accumulates the block tree roots.
Each of these trees would have had a fixed depth (necessary for being able to create
proofs). Chains that integrated Orchard could have decoupled the limits on
commitments-per-subtree from higher-layer constraints like block size, by enabling their
blocks and transactions to be structured internally as a series of Orchard blocks or txs
(e.g. a Zcash block would have contained a `Vec<BlockTreeRoot>`, that each were appended
in-order).
The motivation for considering this change was to improve the lives of light client wallet
developers. When a new note is received, the wallet derives its incremental witness from
the state of the global tree at the point when the note's commitment is appended; this
incremental state then needs to be updated with every subsequent commitment in the block
in-order. Wallets can't get help from the server to create these for new notes without
leaking the specific note that was received.
We decided that this was too large a change from Sapling, and that it should be possible
to improve the Incremental Merkle Tree implementation to work around the efficiency issues
without domain-separating the tree.

View File

@ -1,37 +0,0 @@
# Commitments
As in Sapling, we require two kinds of commitment schemes in Orchard:
- $\mathit{HomomorphicCommit}$ is a linearly homomorphic commitment scheme with perfect hiding,
and strong binding reducible to DL.
- $\mathit{Commit}$ and $\mathit{ShortCommit}$ are commitment schemes with perfect hiding, and
strong binding reducible to DL.
By "strong binding" we mean that the scheme is collision resistant on the input and
randomness.
We instantiate $\mathit{HomomorphicCommit}$ with a Pedersen commitment, and use it for
value commitments:
$$\mathsf{cv} = \mathit{HomomorphicCommit}^{\mathsf{cv}}_{\mathsf{rcv}}(v)$$
We instantiate $\mathit{Commit}$ and $\mathit{ShortCommit}$ with Sinsemilla, and use them
for all other commitments:
$$\mathsf{ivk} = \mathit{ShortCommit}^{\mathsf{ivk}}_{\mathsf{rivk}}(\mathsf{ak}, \mathsf{nk})$$
$$\mathsf{cm} = \mathit{Commit}^{\mathsf{cm}}_{\mathsf{rcm}}(\text{rest of note})$$
This is the same split (and rationale) as in Sapling, but using the more PLONK-efficient
Sinsemilla instead of Bowe--Hopwood Pedersen hashes.
Note that for $\mathsf{ivk}$, we also deviate from Sapling in two ways:
- We use $\mathit{ShortCommit}$ to derive $\mathsf{ivk}$ instead of a full PRF. This removes an
unnecessary (large) PRF primitive from the circuit, at the cost of requiring $\mathsf{rivk}$ to be
part of the full viewing key.
- We define $\mathsf{ivk}$ as an integer in $[1, q_P)$; that is, we exclude $\mathsf{ivk} = 0$. For
Sapling, we relied on BLAKE2s to make $\mathsf{ivk} = 0$ infeasible to produce, but it was still
technically possible. For Orchard, we get this by construction:
- $0$ is not a valid x-coordinate for any Pallas point.
- $\mathsf{SinsemillaShortCommit}$ internally maps points to field elements by replacing the identity (which
has no affine coordinates) with $0$. But $\mathsf{SinsemillaCommit}$ is defined using incomplete addition, and
thus will never produce the identity.

View File

@ -1,110 +0,0 @@
# Keys and addresses
Orchard keys and payment addresses are structurally similar to Sapling. The main change is
that Orchard keys use the Pallas curve instead of Jubjub, in order to enable the future
use of the Pallas-Vesta curve cycle in the Orchard protocol. (We already use Vesta as
the curve on which Halo 2 proofs are computed, but this doesn't yet require a cycle.)
Using the Pallas curve and making the most efficient use of the Halo 2 proof system
involves corresponding changes to the key derivation process, such as using Sinsemilla
for Pallas-efficient commitments. We also take the opportunity to remove all uses of
expensive general-purpose hashes (such as BLAKE2s) from the circuit.
We make several structural changes, building on the lessons learned from Sapling:
- The nullifier private key $\mathsf{nsk}$ is removed. Its purpose in Sapling was as
defense-in-depth, in case RedDSA was found to have weaknesses; an adversary who could
recover $\mathsf{ask}$ would not be able to spend funds. In practice it has not been
feasible to manage $\mathsf{nsk}$ much more securely than a full viewing key, as the
computational power required to generate Sapling proofs has made it necessary to perform
this step on the same device that is creating the overall transaction (rather than on a
more constrained device like a hardware wallet). We are also more confident in RedDSA
now.
- $\mathsf{nk}$ is now a field element instead of a curve point, making it more efficient
to generate nullifiers.
- $\mathsf{ovk}$ is now derived from $\mathsf{fvk}$, instead of being derived in parallel.
This places it in a similar position within the key structure to $\mathsf{ivk}$, and
also removes an issue where two full viewing keys could be constructed that have the
same $\mathsf{ivk}$ but different $\mathsf{ovk}$s. Users still have control over whether
$\mathsf{ovk}$ is used when constructing a transaction.
- All diversifiers now result in valid payment addresses, due to group hashing into Pallas
being specified to be infallible. This removes significant complexity from the use cases
for diversified addresses.
- The fact that Pallas is a prime-order curve simplifies the protocol and removes the need
for cofactor multiplication in key agreement. Unlike Sapling, we define public (including
ephemeral) and private keys used for note encryption to exclude the zero point and the
zero scalar. Without this change, the implementation of the Orchard Action circuit would
need special cases for the zero point, since Pallas is a short Weierstrass rather than
an Edwards curve. This also has the advantage of ensuring that the key agreement has
"contributory behaviour" — that is, if *either* party contributes a random scalar, then
the shared secret will be random to an observer who does not know that scalar and cannot
break DiffieHellman.
Other than the above, Orchard retains the same design rationale for its keys and addresses
as Sapling. For example, diversifiers remain at 11 bytes, so that a raw Orchard address is
the same length as a raw Sapling address.
Orchard payment addresses do not have a stand-alone string encoding. Instead, we define
"unified addresses" that can bundle together addresses of different types, including
Orchard. Unified addresses have a Human-Readable Part of "u" on Mainnet, i.e. they will
have the prefix "u1". For specifications of this and other formats (e.g. for Orchard viewing
and spending keys), see section 5.6.4 of the NU5 protocol specification [#NU5-orchardencodings].
## Hierarchical deterministic wallets
When designing Sapling, we defined a [BIP 32]-like mechanism for generating hierarchical
deterministic wallets in [ZIP 32]. We decided at the time to stick closely to the design
of BIP 32, on the assumption that there were Bitcoin use cases that used both hardened and
non-hardened derivation that we might not be aware of. This decision created significant
complexity for Sapling: we needed to handle derivation separately for each component of
the expanded spending key and full viewing key (whereas for transparent addresses there is
only a single component in the spending key).
Non-hardened derivation enables creating a multi-level path of child addresses below some
parent address, without involving the parent spending key. The primary use case for this
is HD wallets for transparent addresses, which use the following structure defined in
[BIP 44]:
- (H) BIP 44
- (H) Coin type: Zcash
- (H) Account 0
- (N) Normal addresses
- (N) Address 0
- (N) Address 1...
- (N) Change addresses
- (N) Change address 0
- (N) Change address 1...
- (H) Account 1...
Shielded accounts do not require separating change addresses from normal addresses, because
addresses are not revealed in transactions. Similarly, there is also no need to generate
a fresh spending key for every transaction, and in fact this would cause a linear slow-down
in wallet scanning. But for users who do want to generate multiple addresses per account,
they can generate the following structure, which does not use non-hardened derivation:
- (H) ZIP 32
- (H) Coin type: Zcash
- (H) Account 0
- Diversified address 0
- Diversified address 1...
- (H) Account 1...
Non-hardened derivation is therefore only required for use-cases that require the ability
to derive more than one child layer of addresses. However, in the years since Sapling was
deployed, we have not seen *any* such use cases appear.
Therefore, for Orchard we only define hardened derivation, and do so with a much simpler
design than ZIP 32. All derivations produce an opaque binary spending key, from which the
keys and addresses are then derived. As a side benefit, this makes key formats
shorter. (The formats that will actually be used in practice for Orchard will correspond
to the simpler Sapling formats in the protocol specification, rather than the longer
and more complicated "extended" ones defined by ZIP 32.)
[BIP 32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
[BIP 44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
[ZIP 32]: https://zips.z.cash/zip-0032
[NU5-orchardencodings]: https://zips.z.cash/protocol/nu5.pdf#orchardencodings

View File

@ -1,256 +0,0 @@
# Nullifiers
The nullifier design we use for Orchard is
$$\mathsf{nf} = \mathsf{Extract}_{\mathbb{P}}\big([(F_{\mathsf{nk}}(\rho) + \psi) \bmod{p}] \mathcal{G} + \mathsf{cm}\big),$$
where:
- $F$ is a keyed circuit-efficient PRF (such as Rescue or Poseidon).
- $\rho$ is unique to this output. As with $\mathsf{h_{Sig}}$ in Sprout, $\rho$ includes
the nullifiers of any Orchard notes being spent in the same action. Given that an action
consists of a single spend and a single output, we set $\rho$ to be the nullifier of the
spent note.
- $\psi$ is sender-controlled randomness. It is not required to be unique, and in practice
is derived from both $\rho$ and a sender-selected random value $\mathsf{rseed}$:
$$\psi = \mathit{KDF}^\psi(\rho, \mathsf{rseed}).$$
- $\mathcal{G}$ is a fixed independent base.
- $\mathsf{Extract}_{\mathbb{P}}$ extracts the $x$-coordinate of a Pallas curve point.
This gives a note structure of
$$(addr, v, \rho, \psi, \mathsf{rcm}).$$
The note plaintext includes $\mathsf{rseed}$ in place of $\psi$ and $\mathsf{rcm}$, and
omits $\rho$ (which is a public part of the action).
## Security properties
We care about several security properties for our nullifiers:
- **Balance:** can I forge money?
- **Note Privacy:** can I gain information about notes only from the public block chain?
- This describes notes sent in-band.
- **Note Privacy (OOB):** can I gain information about notes sent out-of-band, only from
the public block chain?
- In this case, we assume privacy of the channel over which the note is sent, and that
the adversary does not have access to any notes sent to the same address which are
then spent (so that the nullifier is on the block chain somewhere).
- **Spend Unlinkability:** given the incoming viewing key for an address, and not the full
viewing key, can I (possibly the sender) detect spends of any notes sent to that address?
- We're giving $\mathsf{ivk}$ to the attacker and allowing it to be the sender in order
to make this property as strong as possible: they will have *all* the notes sent to that
address.
- **Faerie Resistance:** can I perform a Faerie Gold attack (i.e. cause notes to be
accepted that are unspendable)?
- We're giving the full viewing key to the attacker and allowing it to be the sender in
order to make this property as strong as possible: they will have *all* the notes sent
to that address, and be able to derive *every* nullifier.
We assume (and instantiate elsewhere) the following primitives:
- $\mathit{GH}$ is a cryptographic hash into the group (such as BLAKE2s with simplified SWU),
used to derive all fixed independent bases.
- $E$ is an elliptic curve (such as Pallas).
- $\mathit{KDF}$ is the note encryption key derivation function.
For our chosen design, our desired security properties rely on the following assumptions:
$$
\begin{array}{|l|l|}
\hline
\text{Balance} & \mathit{DL}_E \\
\text{Note Privacy} & \mathit{HashDH}^{\mathit{KDF}}_E \\
\text{Note Privacy (OOB)} & \text{Near perfect} \ddagger \\
\text{Spend Unlinkability} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F \\
\text{Faerie Resistance} & \mathit{DL}_E \\
\hline
\end{array}
$$
$\mathit{HashDH}^{\mathit{KDF}}_E$ is computational Diffie-Hellman using $\mathit{KDF}$ for
the key derivation, with one-time ephemeral keys. This assumption is heuristically weaker
than $\mathit{DDH}_E$ but stronger than $\mathit{DL}_E$.
We omit $RO_{\mathit{GH}}$ as a security assumption because we only rely on the random oracle
applied to fixed inputs defined by the protocol, i.e. to generate the fixed base
$\mathcal{G}$, not to attacker-specified inputs.
> $\dagger$ We additionally assume that for any input $x$,
> $\{F_{\mathsf{nk}}(x) : \mathsf{nk} \in E\}$ gives a scalar in an adequate range for
> $\mathit{DDH}_E$. (Otherwise, $F$ could be trivial, e.g. independent of $\mathsf{nk}$.)
>
> $\ddagger$ Statistical distance $< 2^{-167.8}$ from perfect.
## Considered alternatives
$\color{red}{\textsf{⚠ Caution}}$: be skeptical of the claims in this table about what
problem(s) each security property depends on. They may not be accurate and are definitely
not fully rigorous.
The entries in this table omit the application of $\mathsf{Extract}_{\mathbb{P}}$,
which is an optimization to halve the nullifier length. That optimization requires its
own security analysis, but because it is a deterministic mapping, only Faerie Resistance
could be affected by it.
$$
\tiny \begin{array}{|c|l|c|c|c|c|c|c|}
\hline\\[-1.5ex]
\mathsf{nf} & \text{Note} & \!\text{Balance}\! & \text{Note Privacy} & \!\text{Note Priv OOB}\! & \!\text{Spend Unlinkability}\! & \text{Faerie Resistance} & \text{Reason not to use} \\[0.6ex]\hline\\[-2.4ex]\hline\\[-1.7ex]
[\mathsf{nk}] [\theta] H & (addr, v, H, \theta, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \text{No SU for DL-breaking}\! \\[0.9ex]\hline\\[-1.7ex]
[\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I} & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \text{No SU for DL-breaking}\! \\[0.9ex]\hline\\[-1.7ex]
\mathit{Hash}([\mathsf{nk}] [\theta] H) & (addr, v, H, \theta, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E \vee \mathit{Pre}_{\mathit{Hash}} & \!\mathit{Coll}_{\mathit{Hash}} \wedge RO_{\mathit{GH}} \wedge \mathit{DL}_E\! & \mathit{Coll}_{\mathit{Hash}} \text{ for FR} \\[0.9ex]\hline\\[-1.7ex]
\mathit{Hash}([\mathsf{nk}] H + [\mathsf{rnf}] \mathcal{I}) & (addr, v, H, \mathsf{rnf}, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E \vee \mathit{Pre}_{\mathit{Hash}} & \!\mathit{Coll}_{\mathit{Hash}} \wedge RO_{\mathit{GH}} \wedge \mathit{DL}_E\! & \mathit{Coll}_{\mathit{Hash}} \text{ for FR} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\psi)] [\theta] H & (addr, v, H, \theta, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \text{Perf. (2 var-base)} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\psi)] H + [\mathsf{rnf}] \mathcal{I} & (addr, v, H, \mathsf{rnf}, \psi, \mathsf{rcm})\! & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \!\text{Perf. (1 var+1 fix-base)}\! \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\psi)] \mathcal{G} + [\theta] H & (addr, v, H, \theta, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \!\text{Perf. (1 var+1 fix-base)}\! \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\psi)] H + \mathsf{cm} & (addr, v, H, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \mathit{DDH}_E^\dagger & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & RO_{\mathit{GH}} \wedge \mathit{DL}_E & \text{NP(OOB) not perfect} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \mathit{DDH}_E^\dagger & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{DL}_E & \text{NP(OOB) not perfect} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\rho)] \mathcal{G} + \mathsf{cm} & (addr, v, \rho, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \mathit{DDH}_E^\dagger & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{DL}_E & \text{NP(OOB) not perfect} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\rho, \psi)] \mathcal{G_v} + [\mathsf{rnf}] \mathcal{I} & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{Coll}_F \wedge \mathit{DL}_E & \mathit{Coll}_F \text{ for FR} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\rho)] \mathcal{G_v} + [\mathsf{rnf}] \mathcal{I} & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{Coll}_F \wedge \mathit{DL}_E & \mathit{Coll}_F \text{ for FR} \\[0.9ex]\hline\\[-1.7ex]
[(F_{\mathsf{nk}}(\rho) + \psi) \bmod{p}] \mathcal{G_v} & (addr, v, \rho, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Near perfect} \ddagger & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \color{red}{\text{broken}} & \text{broken for FR} \\[0.9ex]\hline\\[-1.7ex]
\![F_{\mathsf{nk}}(\rho, \psi)] \mathcal{G} \!+\! \mathit{Commit}^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho)\! & (addr, v, \rho, \mathsf{rnf}, \psi, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{DL}_E & \text{Perf. (2 fix-base)} \\[0.9ex]\hline\\[-1.7ex]
[F_{\mathsf{nk}}(\rho)] \mathcal{G} + \mathit{Commit}^{\mathsf{nf}}_{\mathsf{rnf}}(v, \rho) & (addr, v, \rho, \mathsf{rnf}, \mathsf{rcm}) & \mathit{DL}_E & \mathit{HashDH}^{\mathit{KDF}\!}_E & \text{Perfect} & \mathit{DDH}_E^\dagger \vee \mathit{PRF}_F & \mathit{DL}_E & \text{Perf. (2 fix-base)} \\[0.9ex]\hline
\end{array}
$$
In the above alternatives:
- $\mathit{Hash}$ is a keyed circuit-efficient hash (such as Rescue).
- $\mathcal{I}$ is an fixed independent base, independent of $\mathcal{G}$ and any others
returned by $\mathit{GH}$.
- $\mathcal{G_v}$ is a pair of fixed independent bases (independent of all others), where
the specific choice of base depends on whether the note has zero value.
- $H$ is a base unique to this output.
- For non-zero-valued notes, $H = \mathit{GH}(\rho)$. As with $\mathsf{h_{Sig}}$ in Sprout,
$\rho$ includes the nullifiers of any Orchard notes being spent in the same action.
- For zero-valued notes, $H$ is constrained by the circuit to a fixed base independent
of $\mathcal{I}$ and any others returned by $\mathit{GH}$.
## Rationale
In order to satisfy the **Balance** security property, we require that the circuit must be
able to enforce that only one nullifier is accepted for a given note. As in Sprout and
Sapling, we achieve this by ensuring that the nullifier deterministically depends only on
values committed to (directly or indirectly) by the note commitment. As in Sapling,
this involves arguing that:
- There can be only one $\mathsf{ivk}$ for a given $\mathit{addr}$. This is true because
the circuit checks that $\mathsf{pk_d} = [\mathsf{ivk}] \mathsf{g_d}$, and the mapping
$\mathsf{ivk} \mapsto [\mathsf{ivk}] \mathsf{g_d}$ is an injection for any $\mathsf{g_d}$.
($\mathsf{ivk}$ is in the base field of $E$, which must be smaller than its scalar field,
as is the case for Pallas.)
- There can be only one $\mathsf{nk}$ for a given $\mathsf{ivk}$. This is true because the
circuit checks that $\mathsf{ivk} = \mathit{ShortCommit}^{\mathsf{ivk}}_{\mathsf{rivk}}(\mathsf{ak}, \mathsf{nk})$
where $\mathit{ShortCommit}$ is binding (see [Commitments](commitments.html)).
### Use of $\rho$
**Faerie Resistance** requires that nullifiers be unique. This is primarily achieved by
taking a unique value (checked for uniqueness by the public consensus rules) as an input
to the nullifier. However, it is also necessary to ensure that the transformations applied
to this value preserve its uniqueness. Meanwhile, to achieve **Spend Unlinkability**, we
require that the nullifier does not reveal any information about the unique value it is
derived from.
The design alternatives fall into two categories in terms of how they balance these
requirements:
- Publish a unique value $\rho$ at note creation time, and blind that value within the
nullifier computation.
- This is similar to the approach taken in Sprout and Sapling, which both implemented
nullifiers as PRF outputs; Sprout uses the compression function from SHA-256, while
Sapling uses BLAKE2s.
- Derive a unique base $H$ from some unique value, publish that unique base at note
creation time, and then blind the base (either additively or multiplicatively) during
nullifier computation.
For **Spend Unlinkability**, the only value unknown to the adversary is $\mathsf{nk}$, and
the cryptographic assumptions only involve the first term (other terms like $\mathsf{cm}$
or $[\mathsf{rnf}] \mathcal{I}$ cannot be extracted directly from the observed nullifiers,
but can be subtracted from them). We therefore ensure that the first term does not commit
directly to the note (to avoid a DL-breaking adversary from immediately breaking **SU**).
We were considering using a design involving $H$ with the goal of eliminating all usages
of a PRF inside the circuit, for two reasons:
- Instantiating $\mathit{PRF}_F$ with a traditional hash function is expensive in the
circuit.
- We didn't want to solely rely on an algebraic hash function satisfying $\mathit{PRF}_F$
to achieve **Spend Unlinkability**.
However, those designs rely on both $RO_{\mathit{GH}}$ and $\mathit{DL}_E$ for
**Faerie Resistance**, while still requiring $\mathit{DDH}_E$ for **Spend Unlinkability**.
(There are two designs for which this is not the case, but they rely on
$\mathit{DDH}_E^\dagger$ for **Note Privacy (OOB)** which was not acceptable).
By contrast, several designs involving $\rho$ (including the chosen design) have weaker
assumptions for **Faerie Resistance** (only relying on $\mathit{DL}_E$), and
**Spend Unlinkability** does not require $\mathit{PRF}_F$ to hold: they can fall back
on the same $\mathit{DDH}_E$ assumption as the $H$ designs (along with an additional
assumption about the output of $F$ which is easily satisfied).
### Use of $\psi$
Most of the designs include either a multiplicative blinding term $[\theta] H$, or an
additive blinding term $[\mathsf{rnf}] \mathcal{I}$, in order to achieve perfect
**Note Privacy (OOB)** (to an adversary who does not know the note). The chosen design is
effectively using $[\psi] \mathcal{G}$ for this purpose; a DL-breaking adversary only
learns $F_{\mathsf{nk}}(\rho) + \psi \pmod{p}$. This reduces **Note Privacy (OOB)** from
perfect to statistical, but given that $\psi$ is from a distribution statistically close
to uniform on $[0, q)$, this is statistically close to better than $2^{-128}$. The benefit
is that it does not require an additional scalar multiplication, making it more efficient
inside the circuit.
$\psi$'s derivation has two motivations:
- Deriving from a random value $\mathsf{rseed}$ enables multiple derived values to be
conveyed to the recipient within an action (such as the ephemeral secret $\mathsf{esk}$,
per [ZIP 212](https://zips.z.cash/zip-0212)), while keeping the note plaintext short.
- Mixing $\rho$ into the derivation ensures that the sender can't repeat $\psi$ across two
notes, which could have enabled spend linkability attacks in some designs.
The note that is committed to, and which the circuit takes as input, only includes $\psi$
(i.e. the circuit does not check the derivation from $\mathsf{rseed}$). However, an
adversarial sender is still constrained by this derivation, because the recipient
recomputes $\psi$ during note decryption. If an action were created using an arbitrary
$\psi$ (for which the adversary did not have a corresponding $\mathsf{rseed}$), the
recipient would derive a note commitment that did not match the action's commitment field,
and reject it (as in Sapling).
### Use of $\mathsf{cm}$
The nullifier commits to the note value via $\mathsf{cm}$ for two reasons:
- It domain-separates nullifiers for zero-valued notes from other notes. This is necessary
because we do not require zero-valued notes to exist in the commitment tree.
- Designs that bind the nullifier to $F_{\mathsf{nk}}(\rho)$ require $\mathit{Coll}_F$ to
achieve **Faerie Resistance** (and similarly where $\mathit{Hash}$ is applied to a value
derived from $H$). Adding $\mathsf{cm}$ to the nullifier avoids this assumption: all of
the bases used to derive $\mathsf{cm}$ are fixed and independent of $\mathcal{G}$, and so
the nullifier can be viewed as a Pedersen hash where the input includes $\rho$ directly.
The $\mathit{Commit}^{\mathsf{nf}}$ variants were considered to avoid directly depending on
$\mathsf{cm}$ (which in its native type is a base field element, not a group element). We
decided instead to follow Sapling by defining an intermediate representation of
$\mathsf{cm}$ as a group element, that is only used in nullifier computation. The circuit
already needs to compute $\mathsf{cm}$, so this improves performance by removing
We also considered variants that used a choice of fixed bases $\mathcal{G_v}$ to provide
domain separation for zero-valued notes. The most performant design (similar to the chosen
design) does not achieve **Faerie Resistance** for an adversary that knows the recipient's
full viewing key ($\psi$ could be brute-forced to cancel out $F_{\mathsf{nk}}(\rho)$,
causing a collision), and the other variants require assuming $\mathit{Coll}_F$ as
mentioned above.

View File

@ -1,7 +0,0 @@
# Signatures
Orchard signatures are an instantiation of RedDSA with a cofactor of 1.
TODO:
- Should it be possible to sign partial transactions?
- If we're going to merge down all the signatures into a single one, and also want this, we need to ensure there's a feasible MPC.

View File

@ -1 +0,0 @@
# User Documentation

View File

@ -1 +0,0 @@
# Creating notes

View File

@ -1 +0,0 @@
# Integration into an existing chain

View File

@ -1 +0,0 @@
# Creating keys and addresses

View File

@ -1 +0,0 @@
# Spending notes

View File

@ -1,85 +0,0 @@
use std::convert::TryInto;
use subtle::CtOption;
use crate::{
keys::{DiversifiedTransmissionKey, Diversifier},
spec::{diversify_hash, NonIdentityPallasPoint},
};
/// A shielded payment address.
///
/// # Examples
///
/// ```
/// use orchard::keys::{SpendingKey, FullViewingKey};
///
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
/// let address = FullViewingKey::from(&sk).default_address();
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Address {
d: Diversifier,
pk_d: DiversifiedTransmissionKey,
}
impl Address {
pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self {
// We assume here that pk_d is correctly-derived from d. We ensure this for
// internal APIs. For parsing from raw byte encodings, we assume that users aren't
// modifying internals of encoded address formats. If they do, that can result in
// lost funds, but we can't defend against that from here.
Address { d, pk_d }
}
pub(crate) fn diversifer(&self) -> Diversifier {
self.d
}
pub(crate) fn g_d(&self) -> NonIdentityPallasPoint {
diversify_hash(self.d.as_array())
}
pub(crate) fn pk_d(&self) -> &DiversifiedTransmissionKey {
&self.pk_d
}
/// Serializes this address to its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.2: Orchard Raw Payment Addresses][orchardpaymentaddrencoding]
///
/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#orchardpaymentaddrencoding
pub fn to_raw_address_bytes(&self) -> [u8; 43] {
let mut result = [0u8; 43];
result[..11].copy_from_slice(self.d.as_array());
result[11..].copy_from_slice(&self.pk_d.to_bytes());
result
}
/// Parse an address from its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.2: Orchard Raw Payment Addresses][orchardpaymentaddrencoding]
///
/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#orchardpaymentaddrencoding
pub fn from_raw_address_bytes(bytes: &[u8; 43]) -> CtOption<Self> {
DiversifiedTransmissionKey::from_bytes(bytes[11..].try_into().unwrap()).map(|pk_d| {
let d = Diversifier::from_bytes(bytes[..11].try_into().unwrap());
Self::from_parts(d, pk_d)
})
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use proptest::prelude::*;
use crate::keys::{testing::arb_spending_key, FullViewingKey};
use super::Address;
prop_compose! {
/// Generates an arbitrary payment address.
pub(crate) fn arb_address()(sk in arb_spending_key()) -> Address {
let fvk = FullViewingKey::from(&sk);
fvk.default_address()
}
}
}

View File

@ -1,752 +0,0 @@
//! Logic for building Orchard components of transactions.
use std::convert::TryFrom;
use std::iter;
use ff::Field;
use group::GroupEncoding;
use nonempty::NonEmpty;
use pasta_curves::pallas;
use rand::{CryptoRng, RngCore};
use crate::{
address::Address,
bundle::{Action, Authorization, Authorized, Bundle, Flags},
circuit::{Circuit, Instance, Proof, ProvingKey},
keys::{
FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey, SpendingKey,
},
note::{Note, TransmittedNoteCiphertext},
note_encryption::OrchardNoteEncryption,
primitives::redpallas::{self, Binding, SpendAuth},
tree::{Anchor, MerklePath},
value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum},
};
const MIN_ACTIONS: usize = 2;
/// An error type for the kinds of errors that can occur during bundle construction.
#[derive(Debug)]
pub enum Error {
/// A bundle could not be built because required signatures were missing.
MissingSignatures,
/// An error occurred in the process of producing a proof for a bundle.
Proof(halo2::plonk::Error),
/// An overflow error occurred while attempting to construct the value
/// for a bundle.
ValueSum(value::OverflowError),
}
impl From<halo2::plonk::Error> for Error {
fn from(e: halo2::plonk::Error) -> Self {
Error::Proof(e)
}
}
impl From<value::OverflowError> for Error {
fn from(e: value::OverflowError) -> Self {
Error::ValueSum(e)
}
}
/// Information about a specific note to be spent in an [`Action`].
#[derive(Debug)]
struct SpendInfo {
dummy_sk: Option<SpendingKey>,
fvk: FullViewingKey,
note: Note,
merkle_path: MerklePath,
}
impl SpendInfo {
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
fn dummy(rng: &mut impl RngCore) -> Self {
let (sk, fvk, note) = Note::dummy(rng, None);
let merkle_path = MerklePath::dummy(rng);
SpendInfo {
dummy_sk: Some(sk),
fvk,
note,
merkle_path,
}
}
}
/// Information about a specific recipient to receive funds in an [`Action`].
#[derive(Debug)]
struct RecipientInfo {
ovk: Option<OutgoingViewingKey>,
recipient: Address,
value: NoteValue,
memo: Option<[u8; 512]>,
}
impl RecipientInfo {
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
fn dummy(rng: &mut impl RngCore) -> Self {
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
let recipient = fvk.default_address();
RecipientInfo {
ovk: None,
recipient,
value: NoteValue::zero(),
memo: None,
}
}
}
/// Information about a specific [`Action`] we plan to build.
#[derive(Debug)]
struct ActionInfo {
spend: SpendInfo,
output: RecipientInfo,
rcv: ValueCommitTrapdoor,
}
impl ActionInfo {
fn new(spend: SpendInfo, output: RecipientInfo, rng: impl RngCore) -> Self {
ActionInfo {
spend,
output,
rcv: ValueCommitTrapdoor::random(rng),
}
}
/// Returns the value sum for this action.
fn value_sum(&self) -> Option<ValueSum> {
self.spend.note.value() - self.output.value
}
/// Builds the action.
///
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn build(self, mut rng: impl RngCore) -> (Action<SigningMetadata>, Circuit) {
let v_net = self.value_sum().expect("already checked this");
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
let sender_address = self.spend.fvk.default_address();
let rho_old = self.spend.note.rho();
let psi_old = self.spend.note.rseed().psi(&rho_old);
let rcm_old = self.spend.note.rseed().rcm(&rho_old);
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let note = Note::new(self.output.recipient, self.output.value, nf_old, &mut rng);
let cm_new = note.commitment();
let cmx = cm_new.into();
let encryptor = OrchardNoteEncryption::new(
self.output.ovk,
note,
self.output.recipient,
self.output.memo.unwrap_or_else(|| {
let mut memo = [0; 512];
memo[0] = 0xf6;
memo
}),
);
let encrypted_note = TransmittedNoteCiphertext {
epk_bytes: encryptor.epk().to_bytes().0,
enc_ciphertext: encryptor.encrypt_note_plaintext(),
out_ciphertext: encryptor.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut rng),
};
(
Action::from_parts(
nf_old,
rk,
cmx,
encrypted_note,
cv_net,
SigningMetadata {
dummy_ask: self.spend.dummy_sk.as_ref().map(SpendAuthorizingKey::from),
parts: SigningParts {
ak: ak.clone(),
alpha,
},
},
),
Circuit {
path: Some(self.spend.merkle_path.auth_path()),
pos: Some(self.spend.merkle_path.position()),
g_d_old: Some(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()),
v_old: Some(self.spend.note.value()),
rho_old: Some(rho_old),
psi_old: Some(psi_old),
rcm_old: Some(rcm_old),
cm_old: Some(self.spend.note.commitment()),
alpha: Some(alpha),
ak: Some(ak),
nk: Some(*self.spend.fvk.nk()),
rivk: Some(*self.spend.fvk.rivk()),
g_d_new_star: Some((*note.recipient().g_d()).to_bytes()),
pk_d_new_star: Some(note.recipient().pk_d().to_bytes()),
v_new: Some(note.value()),
psi_new: Some(note.rseed().psi(&note.rho())),
rcm_new: Some(note.rseed().rcm(&note.rho())),
rcv: Some(self.rcv),
},
)
}
}
/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and recipients
/// to receive funds.
#[derive(Debug)]
pub struct Builder {
spends: Vec<SpendInfo>,
recipients: Vec<RecipientInfo>,
flags: Flags,
anchor: Anchor,
}
impl Builder {
/// Constructs a new empty builder for an Orchard bundle.
pub fn new(flags: Flags, anchor: Anchor) -> Self {
Builder {
spends: vec![],
recipients: vec![],
flags,
anchor,
}
}
/// Adds a note to be spent in this transaction.
///
/// Returns an error if the given Merkle path does not have the required anchor for
/// the given note.
pub fn add_spend(
&mut self,
fvk: FullViewingKey,
note: Note,
merkle_path: MerklePath,
) -> Result<(), &'static str> {
if !self.flags.spends_enabled() {
return Err("Spends are not enabled for this builder");
}
// Consistency check: all anchors must be equal.
let cm = note.commitment();
let path_root: Anchor =
<Option<_>>::from(merkle_path.root(cm.into())).ok_or("Derived the bottom anchor")?;
if path_root != self.anchor {
return Err("All anchors must be equal.");
}
self.spends.push(SpendInfo {
dummy_sk: None,
fvk,
note,
merkle_path,
});
Ok(())
}
/// Adds an address which will receive funds in this transaction.
pub fn add_recipient(
&mut self,
ovk: Option<OutgoingViewingKey>,
recipient: Address,
value: NoteValue,
memo: Option<[u8; 512]>,
) -> Result<(), &'static str> {
if !self.flags.outputs_enabled() {
return Err("Outputs are not enabled for this builder");
}
self.recipients.push(RecipientInfo {
ovk,
recipient,
value,
memo,
});
Ok(())
}
/// Builds a bundle containing the given spent notes and recipients.
///
/// This API assumes that none of the notes being spent are controlled by (threshold)
/// multisignatures, and immediately constructs the bundle proof.
pub fn build<V: TryFrom<i64>>(
mut self,
mut rng: impl RngCore,
) -> Result<Bundle<InProgress<Unproven, Unauthorized>, V>, Error> {
// Pair up the spends and recipients, extending with dummy values as necessary.
//
// TODO: Do we want to shuffle the order like we do for Sapling? And if we do, do
// we need the extra logic for mapping the user-provided input order to the
// shuffled order?
let pre_actions: Vec<_> = {
let num_spends = self.spends.len();
let num_recipients = self.recipients.len();
let num_actions = [num_spends, num_recipients, MIN_ACTIONS]
.iter()
.max()
.cloned()
.unwrap();
self.spends.extend(
iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends),
);
self.recipients.extend(
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
.take(num_actions - num_recipients),
);
self.spends
.into_iter()
.zip(self.recipients.into_iter())
.map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng))
.collect()
};
// Move some things out of self that we will need.
let flags = self.flags;
let anchor = self.anchor;
// Determine the value balance for this bundle, ensuring it is valid.
let value_balance = pre_actions
.iter()
.fold(Some(ValueSum::zero()), |acc, action| {
acc? + action.value_sum()?
})
.ok_or(OverflowError)?;
let result_value_balance: V = i64::try_from(value_balance)
.map_err(Error::ValueSum)
.and_then(|i| V::try_from(i).map_err(|_| Error::ValueSum(value::OverflowError)))?;
// Compute the transaction binding signing key.
let bsk = pre_actions
.iter()
.map(|a| &a.rcv)
.sum::<ValueCommitTrapdoor>()
.into_bsk();
// Create the actions.
let (actions, circuits): (Vec<_>, Vec<_>) =
pre_actions.into_iter().map(|a| a.build(&mut rng)).unzip();
// Verify that bsk and bvk are consistent.
let bvk = (actions.iter().map(|a| a.cv_net()).sum::<ValueCommitment>()
- ValueCommitment::derive(value_balance, ValueCommitTrapdoor::zero()))
.into_bvk();
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
Ok(Bundle::from_parts(
NonEmpty::from_vec(actions).unwrap(),
flags,
result_value_balance,
anchor,
InProgress {
proof: Unproven { circuits },
sigs: Unauthorized { bsk },
},
))
}
}
/// Marker trait representing bundle signatures in the process of being created.
pub trait InProgressSignatures {
/// The authorization type of an Orchard action in the process of being authorized.
type SpendAuth;
}
/// Marker for a bundle in the process of being built.
#[derive(Debug)]
pub struct InProgress<P, S: InProgressSignatures> {
proof: P,
sigs: S,
}
impl<P, S: InProgressSignatures> Authorization for InProgress<P, S> {
type SpendAuth = S::SpendAuth;
}
/// Marker for a bundle without a proof.
///
/// This struct contains the private data needed to create a [`Proof`] for a [`Bundle`].
#[derive(Debug)]
pub struct Unproven {
circuits: Vec<Circuit>,
}
impl<S: InProgressSignatures> InProgress<Unproven, S> {
/// Creates the proof for this bundle.
pub fn create_proof(
&self,
pk: &ProvingKey,
instances: &[Instance],
rng: impl RngCore,
) -> Result<Proof, halo2::plonk::Error> {
Proof::create(pk, &self.proof.circuits, instances, rng)
}
}
impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
/// Creates the proof for this bundle.
pub fn create_proof(
self,
pk: &ProvingKey,
mut rng: impl RngCore,
) -> Result<Bundle<InProgress<Proof, S>, V>, Error> {
let instances: Vec<_> = self
.actions()
.iter()
.map(|a| a.to_instance(*self.flags(), *self.anchor()))
.collect();
self.try_authorize(
&mut (),
|_, _, a| Ok(a),
|_, auth| {
let proof = auth.create_proof(pk, &instances, &mut rng)?;
Ok(InProgress {
proof,
sigs: auth.sigs,
})
},
)
}
}
/// The parts needed to sign an [`Action`].
#[derive(Debug)]
pub struct SigningParts {
/// The spend validating key for this action. Used to match spend authorizing keys to
/// actions they can create signatures for.
ak: SpendValidatingKey,
/// The randomization needed to derive the actual signing key for this note.
alpha: pallas::Scalar,
}
/// Marker for an unauthorized bundle with no signatures.
#[derive(Debug)]
pub struct Unauthorized {
bsk: redpallas::SigningKey<Binding>,
}
impl InProgressSignatures for Unauthorized {
type SpendAuth = SigningMetadata;
}
/// Container for metadata needed to sign an [`Action`].
#[derive(Debug)]
pub struct SigningMetadata {
/// If this action is spending a dummy note, this field holds that note's spend
/// authorizing key.
///
/// These keys are used automatically in [`Bundle<Unauthorized>::prepare`] or
/// [`Bundle<Unauthorized>::apply_signatures`] to sign dummy spends.
dummy_ask: Option<SpendAuthorizingKey>,
parts: SigningParts,
}
/// Marker for a partially-authorized bundle, in the process of being signed.
#[derive(Debug)]
pub struct PartiallyAuthorized {
binding_signature: redpallas::Signature<Binding>,
sighash: [u8; 32],
}
impl InProgressSignatures for PartiallyAuthorized {
type SpendAuth = MaybeSigned;
}
/// A heisen[`Signature`] for a particular [`Action`].
///
/// [`Signature`]: redpallas::Signature
#[derive(Debug)]
pub enum MaybeSigned {
/// The information needed to sign this [`Action`].
SigningMetadata(SigningParts),
/// The signature for this [`Action`].
Signature(redpallas::Signature<SpendAuth>),
}
impl MaybeSigned {
fn finalize(self) -> Result<redpallas::Signature<SpendAuth>, Error> {
match self {
Self::Signature(sig) => Ok(sig),
_ => Err(Error::MissingSignatures),
}
}
}
impl<P, V> Bundle<InProgress<P, Unauthorized>, V> {
/// Loads the sighash into this bundle, preparing it for signing.
///
/// This API ensures that all signatures are created over the same sighash.
pub fn prepare<R: RngCore + CryptoRng>(
self,
mut rng: R,
sighash: [u8; 32],
) -> Bundle<InProgress<P, PartiallyAuthorized>, V> {
self.authorize(
&mut rng,
|rng, _, SigningMetadata { dummy_ask, parts }| {
// We can create signatures for dummy spends immediately.
dummy_ask
.map(|ask| ask.randomize(&parts.alpha).sign(rng, &sighash))
.map(MaybeSigned::Signature)
.unwrap_or(MaybeSigned::SigningMetadata(parts))
},
|rng, auth| InProgress {
proof: auth.proof,
sigs: PartiallyAuthorized {
binding_signature: auth.sigs.bsk.sign(rng, &sighash),
sighash,
},
},
)
}
}
impl<V> Bundle<InProgress<Proof, Unauthorized>, V> {
/// Applies signatures to this bundle, in order to authorize it.
pub fn apply_signatures<R: RngCore + CryptoRng>(
self,
mut rng: R,
sighash: [u8; 32],
signing_keys: &[SpendAuthorizingKey],
) -> Result<Bundle<Authorized, V>, Error> {
signing_keys
.iter()
.fold(self.prepare(&mut rng, sighash), |partial, ask| {
partial.sign(&mut rng, ask)
})
.finalize()
}
}
impl<P, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
/// Signs this bundle with the given [`SpendAuthorizingKey`].
///
/// This will apply signatures for all notes controlled by this spending key.
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &SpendAuthorizingKey) -> Self {
let expected_ak = ask.into();
self.authorize(
&mut rng,
|rng, partial, maybe| match maybe {
MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => {
MaybeSigned::Signature(
ask.randomize(&parts.alpha).sign(rng, &partial.sigs.sighash),
)
}
s => s,
},
|_, partial| partial,
)
}
}
impl<V> Bundle<InProgress<Proof, PartiallyAuthorized>, V> {
/// Finalizes this bundle, enabling it to be included in a transaction.
///
/// Returns an error if any signatures are missing.
pub fn finalize(self) -> Result<Bundle<Authorized, V>, Error> {
self.try_authorize(
&mut (),
|_, _, maybe| maybe.finalize(),
|_, partial| {
Ok(Authorized::from_parts(
partial.proof,
partial.sigs.binding_signature,
))
},
)
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Tree};
use rand::{rngs::StdRng, CryptoRng, SeedableRng};
use std::convert::TryFrom;
use std::fmt::Debug;
use proptest::collection::vec;
use proptest::prelude::*;
use crate::{
address::testing::arb_address,
bundle::{Authorized, Bundle, Flags},
circuit::ProvingKey,
keys::{
testing::arb_spending_key, FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey,
SpendingKey,
},
note::testing::arb_note,
tree::{Anchor, MerkleHashOrchard, MerklePath},
value::{testing::arb_positive_note_value, NoteValue, MAX_NOTE_VALUE},
Address, Note,
};
use super::Builder;
/// An intermediate type used for construction of arbitrary
/// bundle values. This type is required because of a limitation
/// of the proptest prop_compose! macro which does not correctly
/// handle polymorphic generator functions. Instead of generating
/// a bundle directly, we generate the bundle inputs, and then
/// are able to use the `build` function to construct the bundle
/// from these inputs, but using a `ValueBalance` implementation that
/// is defined by the end user.
#[derive(Debug)]
struct ArbitraryBundleInputs<R> {
rng: R,
sk: SpendingKey,
anchor: Anchor,
notes: Vec<(Note, MerklePath)>,
recipient_amounts: Vec<(Address, NoteValue)>,
}
impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
/// Create a bundle from the set of arbitrary bundle inputs.
fn into_bundle<V: TryFrom<i64>>(mut self) -> Bundle<Authorized, V> {
let fvk = FullViewingKey::from(&self.sk);
let ovk = OutgoingViewingKey::from(&fvk);
let flags = Flags::from_parts(true, true);
let mut builder = Builder::new(flags, self.anchor);
for (note, path) in self.notes.into_iter() {
builder.add_spend(fvk.clone(), note, path).unwrap();
}
for (addr, value) in self.recipient_amounts.into_iter() {
builder
.add_recipient(Some(ovk.clone()), addr, value, None)
.unwrap();
}
let pk = ProvingKey::build();
builder
.build(&mut self.rng)
.unwrap()
.create_proof(&pk, &mut self.rng)
.unwrap()
.prepare(&mut self.rng, [0; 32])
.sign(&mut self.rng, &SpendAuthorizingKey::from(&self.sk))
.finalize()
.unwrap()
}
}
prop_compose! {
/// Produce a random valid Orchard bundle.
fn arb_bundle_inputs(sk: SpendingKey)
(
n_notes in 1usize..30,
n_recipients in 1..30,
)
(
// generate note values that we're certain won't exceed MAX_NOTE_VALUE in total
notes in vec(
arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note),
n_notes
),
recipient_amounts in vec(
arb_address().prop_flat_map(move |a| {
arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64)
.prop_map(move |v| (a, v))
}),
n_recipients as usize
),
rng_seed in prop::array::uniform32(prop::num::u8::ANY)
) -> ArbitraryBundleInputs<StdRng> {
const MERKLE_DEPTH_ORCHARD: u8 = crate::constants::MERKLE_DEPTH_ORCHARD as u8;
let mut tree = BridgeTree::<MerkleHashOrchard, MERKLE_DEPTH_ORCHARD>::new(100);
let mut notes_and_auth_paths: Vec<(Note, MerklePath)> = Vec::new();
for note in notes.iter() {
let leaf = MerkleHashOrchard::from_cmx(&note.commitment().into());
tree.append(&leaf);
tree.witness();
let path = tree.authentication_path(&leaf).unwrap().into();
notes_and_auth_paths.push((*note, path));
}
ArbitraryBundleInputs {
rng: StdRng::from_seed(rng_seed),
sk,
anchor: tree.root().into(),
notes: notes_and_auth_paths,
recipient_amounts
}
}
}
/// Produce an arbitrary valid Orchard bundle using a random spending key.
pub fn arb_bundle<V: TryFrom<i64> + Debug>() -> impl Strategy<Value = Bundle<Authorized, V>> {
arb_spending_key()
.prop_flat_map(arb_bundle_inputs)
.prop_map(|inputs| inputs.into_bundle::<V>())
}
/// Produce an arbitrary valid Orchard bundle using a specified spending key.
pub fn arb_bundle_with_key<V: TryFrom<i64> + Debug>(
k: SpendingKey,
) -> impl Strategy<Value = Bundle<Authorized, V>> {
arb_bundle_inputs(k).prop_map(|inputs| inputs.into_bundle::<V>())
}
}
#[cfg(test)]
mod tests {
use rand::rngs::OsRng;
use super::Builder;
use crate::{
bundle::{Authorized, Bundle, Flags},
circuit::ProvingKey,
constants::MERKLE_DEPTH_ORCHARD,
keys::{FullViewingKey, SpendingKey},
tree::EMPTY_ROOTS,
value::NoteValue,
};
#[test]
fn shielding_bundle() {
let pk = ProvingKey::build();
let mut rng = OsRng;
let sk = SpendingKey::random(&mut rng);
let fvk = FullViewingKey::from(&sk);
let recipient = fvk.default_address();
let mut builder = Builder::new(
Flags::from_parts(true, true),
EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(),
);
builder
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
.unwrap();
let bundle: Bundle<Authorized, i64> = builder
.build(&mut rng)
.unwrap()
.create_proof(&pk, &mut rng)
.unwrap()
.prepare(&mut rng, [0; 32])
.finalize()
.unwrap();
assert_eq!(bundle.value_balance(), &(-5000))
}
}

View File

@ -1,735 +0,0 @@
//! Structs related to bundles of Orchard actions.
pub mod commitments;
use std::io;
use blake2b_simd::Hash as Blake2bHash;
use memuse::DynamicUsage;
use nonempty::NonEmpty;
use zcash_note_encryption::try_note_decryption;
use crate::{
address::Address,
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
circuit::{Instance, Proof, VerifyingKey},
keys::IncomingViewingKey,
note::{ExtractedNoteCommitment, Note, Nullifier, TransmittedNoteCiphertext},
note_encryption::OrchardDomain,
primitives::redpallas::{self, Binding, SpendAuth},
tree::Anchor,
value::{ValueCommitTrapdoor, ValueCommitment, ValueSum},
};
/// An action applied to the global ledger.
///
/// Externally, this both creates a note (adding a commitment to the global ledger),
/// and consumes some note created prior to this action (adding a nullifier to the
/// global ledger).
///
/// Internally, this may both consume a note and create a note, or it may do only one of
/// the two. TODO: Determine which is more efficient (circuit size vs bundle size).
#[derive(Debug, Clone)]
pub struct Action<A> {
/// The nullifier of the note being spent.
nf: Nullifier,
/// The randomized verification key for the note being spent.
rk: redpallas::VerificationKey<SpendAuth>,
/// A commitment to the new note being created.
cmx: ExtractedNoteCommitment,
/// The transmitted note ciphertext.
encrypted_note: TransmittedNoteCiphertext,
/// A commitment to the net value created or consumed by this action.
cv_net: ValueCommitment,
/// The authorization for this action.
authorization: A,
}
impl<T> Action<T> {
/// Constructs an `Action` from its constituent parts.
pub fn from_parts(
nf: Nullifier,
rk: redpallas::VerificationKey<SpendAuth>,
cmx: ExtractedNoteCommitment,
encrypted_note: TransmittedNoteCiphertext,
cv_net: ValueCommitment,
authorization: T,
) -> Self {
Action {
nf,
rk,
cmx,
encrypted_note,
cv_net,
authorization,
}
}
/// Returns the nullifier of the note being spent.
pub fn nullifier(&self) -> &Nullifier {
&self.nf
}
/// Returns the randomized verification key for the note being spent.
pub fn rk(&self) -> &redpallas::VerificationKey<SpendAuth> {
&self.rk
}
/// Returns the commitment to the new note being created.
pub fn cmx(&self) -> &ExtractedNoteCommitment {
&self.cmx
}
/// Returns the encrypted note ciphertext.
pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext {
&self.encrypted_note
}
/// Returns the commitment to the net value created or consumed by this action.
pub fn cv_net(&self) -> &ValueCommitment {
&self.cv_net
}
/// Returns the authorization for this action.
pub fn authorization(&self) -> &T {
&self.authorization
}
/// Transitions this action from one authorization state to another.
pub fn map<U>(self, step: impl FnOnce(T) -> U) -> Action<U> {
Action {
nf: self.nf,
rk: self.rk,
cmx: self.cmx,
encrypted_note: self.encrypted_note,
cv_net: self.cv_net,
authorization: step(self.authorization),
}
}
/// Transitions this action from one authorization state to another.
pub fn try_map<U, E>(self, step: impl FnOnce(T) -> Result<U, E>) -> Result<Action<U>, E> {
Ok(Action {
nf: self.nf,
rk: self.rk,
cmx: self.cmx,
encrypted_note: self.encrypted_note,
cv_net: self.cv_net,
authorization: step(self.authorization)?,
})
}
/// Prepares the public instance for this action, for creating and verifying the
/// bundle proof.
pub fn to_instance(&self, flags: Flags, anchor: Anchor) -> Instance {
Instance {
anchor,
cv_net: self.cv_net.clone(),
nf_old: self.nf,
rk: self.rk.clone(),
cmx: self.cmx,
enable_spend: flags.spends_enabled,
enable_output: flags.outputs_enabled,
}
}
}
impl DynamicUsage for Action<redpallas::Signature<SpendAuth>> {
#[inline(always)]
fn dynamic_usage(&self) -> usize {
0
}
#[inline(always)]
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
}
/// Orchard-specific flags.
#[derive(Clone, Copy, Debug)]
pub struct Flags {
/// Flag denoting whether Orchard spends are enabled in the transaction.
///
/// If `false`, spent notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be dummy notes. If `true`, the spent notes may be either real or
/// dummy notes.
spends_enabled: bool,
/// Flag denoting whether Orchard outputs are enabled in the transaction.
///
/// If `false`, created notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be dummy notes. If `true`, the created notes may be either real or
/// dummy notes.
outputs_enabled: bool,
}
const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001;
const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010;
const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED);
impl Flags {
/// Construct a set of flags from its constituent parts
pub fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self {
Flags {
spends_enabled,
outputs_enabled,
}
}
/// Flag denoting whether Orchard spends are enabled in the transaction.
///
/// If `false`, spent notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be dummy notes. If `true`, the spent notes may be either real or
/// dummy notes.
pub fn spends_enabled(&self) -> bool {
self.spends_enabled
}
/// Flag denoting whether Orchard outputs are enabled in the transaction.
///
/// If `false`, created notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be dummy notes. If `true`, the created notes may be either real or
/// dummy notes.
pub fn outputs_enabled(&self) -> bool {
self.outputs_enabled
}
/// Serialize flags to a byte as defined in [Zcash Protocol Spec § 7.1: Transaction
/// Encoding And Consensus][txencoding].
///
/// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding
pub fn to_byte(&self) -> u8 {
let mut value = 0u8;
if self.spends_enabled {
value |= FLAG_SPENDS_ENABLED;
}
if self.outputs_enabled {
value |= FLAG_OUTPUTS_ENABLED;
}
value
}
/// Parse from a single byte as defined in [Zcash Protocol Spec § 7.1: Transaction
/// Encoding And Consensus][txencoding].
///
/// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding
pub fn from_byte(value: u8) -> io::Result<Self> {
if value & FLAGS_EXPECTED_UNSET == 0 {
Ok(Self::from_parts(
value & FLAG_SPENDS_ENABLED != 0,
value & FLAG_OUTPUTS_ENABLED != 0,
))
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Unexpected bits set in Orchard flags value.",
))
}
}
}
/// Defines the authorization type of an Orchard bundle.
pub trait Authorization {
/// The authorization type of an Orchard action.
type SpendAuth;
}
/// A bundle of actions to be applied to the ledger.
#[derive(Debug, Clone)]
pub struct Bundle<T: Authorization, V> {
/// The list of actions that make up this bundle.
actions: NonEmpty<Action<T::SpendAuth>>,
/// Orchard-specific transaction-level flags for this bundle.
flags: Flags,
/// The net value moved out of the Orchard shielded pool.
///
/// This is the sum of Orchard spends minus the sum of Orchard outputs.
value_balance: V,
/// The root of the Orchard commitment tree that this bundle commits to.
anchor: Anchor,
/// The authorization for this bundle.
authorization: T,
}
impl<T: Authorization, V> Bundle<T, V> {
/// Constructs a `Bundle` from its constituent parts.
pub fn from_parts(
actions: NonEmpty<Action<T::SpendAuth>>,
flags: Flags,
value_balance: V,
anchor: Anchor,
authorization: T,
) -> Self {
Bundle {
actions,
flags,
value_balance,
anchor,
authorization,
}
}
/// Returns the list of actions that make up this bundle.
pub fn actions(&self) -> &NonEmpty<Action<T::SpendAuth>> {
&self.actions
}
/// Returns the Orchard-specific transaction-level flags for this bundle.
pub fn flags(&self) -> &Flags {
&self.flags
}
/// Returns the net value moved into or out of the Orchard shielded pool.
///
/// This is the sum of Orchard spends minus the sum Orchard outputs.
pub fn value_balance(&self) -> &V {
&self.value_balance
}
/// Returns the root of the Orchard commitment tree that this bundle commits to.
pub fn anchor(&self) -> &Anchor {
&self.anchor
}
/// Returns the authorization for this bundle.
///
/// In the case of a `Bundle<Authorized>`, this is the proof and binding signature.
pub fn authorization(&self) -> &T {
&self.authorization
}
/// Computes a commitment to the effects of this bundle, suitable for inclusion within
/// a transaction ID.
pub fn commitment<'a>(&'a self) -> BundleCommitment
where
i64: From<&'a V>,
{
BundleCommitment(hash_bundle_txid_data(self))
}
/// Construct a new bundle by applying a transformation that might fail
/// to the value balance.
pub fn try_map_value_balance<V0, E, F: FnOnce(V) -> Result<V0, E>>(
self,
f: F,
) -> Result<Bundle<T, V0>, E> {
Ok(Bundle {
actions: self.actions,
flags: self.flags,
value_balance: f(self.value_balance)?,
anchor: self.anchor,
authorization: self.authorization,
})
}
/// Transitions this bundle from one authorization state to another.
pub fn authorize<R, U: Authorization>(
self,
context: &mut R,
mut spend_auth: impl FnMut(&mut R, &T, T::SpendAuth) -> U::SpendAuth,
step: impl FnOnce(&mut R, T) -> U,
) -> Bundle<U, V> {
let authorization = self.authorization;
Bundle {
actions: self
.actions
.map(|a| a.map(|a_auth| spend_auth(context, &authorization, a_auth))),
flags: self.flags,
value_balance: self.value_balance,
anchor: self.anchor,
authorization: step(context, authorization),
}
}
/// Transitions this bundle from one authorization state to another.
pub fn try_authorize<R, U: Authorization, E>(
self,
context: &mut R,
mut spend_auth: impl FnMut(&mut R, &T, T::SpendAuth) -> Result<U::SpendAuth, E>,
step: impl FnOnce(&mut R, T) -> Result<U, E>,
) -> Result<Bundle<U, V>, E> {
let authorization = self.authorization;
let new_actions = self
.actions
.into_iter()
.map(|a| a.try_map(|a_auth| spend_auth(context, &authorization, a_auth)))
.collect::<Result<Vec<_>, E>>()?;
Ok(Bundle {
actions: NonEmpty::from_vec(new_actions).unwrap(),
flags: self.flags,
value_balance: self.value_balance,
anchor: self.anchor,
authorization: step(context, authorization)?,
})
}
pub(crate) fn to_instances(&self) -> Vec<Instance> {
self.actions
.iter()
.map(|a| a.to_instance(self.flags, self.anchor))
.collect()
}
/// Perform trial decryption of each action in the bundle with each of the
/// specified incoming viewing keys, and return the decrypted note contents
/// along with the index of the action from which it was derived.
pub fn decrypt_outputs_for_keys(
&self,
keys: &[IncomingViewingKey],
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
self.actions
.iter()
.enumerate()
.filter_map(|(idx, action)| {
let domain = OrchardDomain::for_action(action);
keys.iter().find_map(move |ivk| {
try_note_decryption(&domain, ivk, action)
.map(|(n, a, m)| (idx, ivk.clone(), n, a, m))
})
})
.collect()
}
/// Perform trial decryption of each action at `action_idx` in the bundle with the
/// specified incoming viewing key, and return the decrypted note contents.
pub fn decrypt_output_with_key(
&self,
action_idx: usize,
key: &IncomingViewingKey,
) -> Option<(Note, Address, [u8; 512])> {
self.actions.get(action_idx).and_then(move |action| {
let domain = OrchardDomain::for_action(action);
try_note_decryption(&domain, key, action)
})
}
}
impl<T: Authorization, V: Copy + Into<ValueSum>> Bundle<T, V> {
/// Returns the transaction binding validating key for this bundle.
///
/// This can be used to validate the [`Authorized::binding_signature`] returned from
/// [`Bundle::authorization`].
pub fn binding_validating_key(&self) -> redpallas::VerificationKey<Binding> {
(self
.actions
.iter()
.map(|a| a.cv_net())
.sum::<ValueCommitment>()
- ValueCommitment::derive(self.value_balance.into(), ValueCommitTrapdoor::zero()))
.into_bvk()
}
}
/// Authorizing data for a bundle of actions, ready to be committed to the ledger.
#[derive(Debug, Clone)]
pub struct Authorized {
proof: Proof,
binding_signature: redpallas::Signature<Binding>,
}
impl Authorization for Authorized {
type SpendAuth = redpallas::Signature<SpendAuth>;
}
impl Authorized {
/// Constructs the authorizing data for a bundle of actions from its constituent parts.
pub fn from_parts(proof: Proof, binding_signature: redpallas::Signature<Binding>) -> Self {
Authorized {
proof,
binding_signature,
}
}
/// Return the proof component of the authorizing data.
pub fn proof(&self) -> &Proof {
&self.proof
}
/// Return the binding signature.
pub fn binding_signature(&self) -> &redpallas::Signature<Binding> {
&self.binding_signature
}
}
impl<V> Bundle<Authorized, V> {
/// Computes a commitment to the authorizing data within for this bundle.
///
/// This together with `Bundle::commitment` bind the entire bundle.
pub fn authorizing_commitment(&self) -> BundleAuthorizingCommitment {
BundleAuthorizingCommitment(hash_bundle_auth_data(self))
}
/// Verifies the proof for this bundle.
pub fn verify_proof(&self, vk: &VerifyingKey) -> Result<(), halo2::plonk::Error> {
self.authorization()
.proof()
.verify(vk, &self.to_instances())
}
}
impl<V: DynamicUsage> DynamicUsage for Bundle<Authorized, V> {
fn dynamic_usage(&self) -> usize {
self.actions.dynamic_usage()
+ self.value_balance.dynamic_usage()
+ self.authorization.proof.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let bounds = (
self.actions.dynamic_usage_bounds(),
self.value_balance.dynamic_usage_bounds(),
self.authorization.proof.dynamic_usage_bounds(),
);
(
bounds.0 .0 + bounds.1 .0 + bounds.2 .0,
bounds
.0
.1
.zip(bounds.1 .1)
.zip(bounds.2 .1)
.map(|((a, b), c)| a + b + c),
)
}
}
/// A commitment to a bundle of actions.
///
/// This commitment is non-malleable, in the sense that a bundle's commitment will only
/// change if the effects of the bundle are altered.
#[derive(Debug)]
pub struct BundleCommitment(pub Blake2bHash);
/// A commitment to the authorizing data within a bundle of actions.
#[derive(Debug)]
pub struct BundleAuthorizingCommitment(pub Blake2bHash);
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use nonempty::NonEmpty;
use pasta_curves::{arithmetic::FieldExt, pallas};
use rand::{rngs::StdRng, SeedableRng};
use reddsa::orchard::SpendAuth;
use proptest::collection::vec;
use proptest::prelude::*;
use crate::{
circuit::Proof,
note::{
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
testing::arb_note, TransmittedNoteCiphertext,
},
primitives::redpallas::{
self,
testing::{
arb_binding_signing_key, arb_spendauth_signing_key, arb_spendauth_verification_key,
},
},
value::{
testing::arb_note_value_bounded, NoteValue, ValueCommitTrapdoor, ValueCommitment,
ValueSum, MAX_NOTE_VALUE,
},
Anchor,
};
use super::{Action, Authorization, Authorized, Bundle, Flags};
/// Marker for an unauthorized bundle with no proofs or signatures.
#[derive(Debug)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type SpendAuth = ();
}
prop_compose! {
/// Generate an action without authorization data.
pub fn arb_unauthorized_action(spend_value: NoteValue, output_value: NoteValue)(
nf in arb_nullifier(),
rk in arb_spendauth_verification_key(),
note in arb_note(output_value),
) -> Action<()> {
let cmx = ExtractedNoteCommitment::from(note.commitment());
let cv_net = ValueCommitment::derive(
(spend_value - output_value).unwrap(),
ValueCommitTrapdoor::zero()
);
// FIXME: make a real one from the note.
let encrypted_note = TransmittedNoteCiphertext {
epk_bytes: [0u8; 32],
enc_ciphertext: [0u8; 580],
out_ciphertext: [0u8; 80]
};
Action {
nf,
rk,
cmx,
encrypted_note,
cv_net,
authorization: ()
}
}
}
/// Generate an unauthorized action having spend and output values less than MAX_NOTE_VALUE / n_actions.
pub fn arb_unauthorized_action_n(
n_actions: usize,
flags: Flags,
) -> impl Strategy<Value = (ValueSum, Action<()>)> {
let spend_value_gen = if flags.spends_enabled {
Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64))
} else {
Strategy::boxed(Just(NoteValue::zero()))
};
spend_value_gen.prop_flat_map(move |spend_value| {
let output_value_gen = if flags.outputs_enabled {
Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64))
} else {
Strategy::boxed(Just(NoteValue::zero()))
};
output_value_gen.prop_flat_map(move |output_value| {
arb_unauthorized_action(spend_value, output_value)
.prop_map(move |a| ((spend_value - output_value).unwrap(), a))
})
})
}
prop_compose! {
/// Generate an action with invalid (random) authorization data.
pub fn arb_action(spend_value: NoteValue, output_value: NoteValue)(
nf in arb_nullifier(),
sk in arb_spendauth_signing_key(),
note in arb_note(output_value),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
) -> Action<redpallas::Signature<SpendAuth>> {
let cmx = ExtractedNoteCommitment::from(note.commitment());
let cv_net = ValueCommitment::derive(
(spend_value - output_value).unwrap(),
ValueCommitTrapdoor::zero()
);
// FIXME: make a real one from the note.
let encrypted_note = TransmittedNoteCiphertext {
epk_bytes: [0u8; 32],
enc_ciphertext: [0u8; 580],
out_ciphertext: [0u8; 80]
};
let rng = StdRng::from_seed(rng_seed);
Action {
nf,
rk: redpallas::VerificationKey::from(&sk),
cmx,
encrypted_note,
cv_net,
authorization: sk.sign(rng, &fake_sighash),
}
}
}
/// Generate an authorized action having spend and output values less than MAX_NOTE_VALUE / n_actions.
pub fn arb_action_n(
n_actions: usize,
flags: Flags,
) -> impl Strategy<Value = (ValueSum, Action<redpallas::Signature<SpendAuth>>)> {
let spend_value_gen = if flags.spends_enabled {
Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64))
} else {
Strategy::boxed(Just(NoteValue::zero()))
};
spend_value_gen.prop_flat_map(move |spend_value| {
let output_value_gen = if flags.outputs_enabled {
Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64))
} else {
Strategy::boxed(Just(NoteValue::zero()))
};
output_value_gen.prop_flat_map(move |output_value| {
arb_action(spend_value, output_value)
.prop_map(move |a| ((spend_value - output_value).unwrap(), a))
})
})
}
prop_compose! {
/// Create an arbitrary set of flags.
pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY) -> Flags {
Flags::from_parts(spends_enabled, outputs_enabled)
}
}
prop_compose! {
fn arb_base()(bytes in prop::array::uniform32(0u8..)) -> pallas::Base {
// Instead of rejecting out-of-range bytes, let's reduce them.
let mut buf = [0; 64];
buf[..32].copy_from_slice(&bytes);
pallas::Base::from_bytes_wide(&buf)
}
}
prop_compose! {
/// Generate an arbitrary unauthorized bundle. This bundle does not
/// necessarily respect consensus rules; for that use
/// [`crate::builder::testing::arb_bundle`]
pub fn arb_unauthorized_bundle(n_actions: usize)
(
flags in arb_flags(),
)
(
acts in vec(arb_unauthorized_action_n(n_actions, flags), n_actions),
anchor in arb_base().prop_map(Anchor::from),
flags in Just(flags)
) -> Bundle<Unauthorized, ValueSum> {
let (balances, actions): (Vec<ValueSum>, Vec<Action<_>>) = acts.into_iter().unzip();
Bundle::from_parts(
NonEmpty::from_vec(actions).unwrap(),
flags,
balances.into_iter().sum::<Result<ValueSum, _>>().unwrap(),
anchor,
Unauthorized
)
}
}
prop_compose! {
/// Generate an arbitrary bundle with fake authorization data. This bundle does not
/// necessarily respect consensus rules; for that use
/// [`crate::builder::testing::arb_bundle`]
pub fn arb_bundle(n_actions: usize)
(
flags in arb_flags(),
)
(
acts in vec(arb_action_n(n_actions, flags), n_actions),
anchor in arb_base().prop_map(Anchor::from),
sk in arb_binding_signing_key(),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_proof in vec(prop::num::u8::ANY, 1973),
fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
flags in Just(flags)
) -> Bundle<Authorized, ValueSum> {
let (balances, actions): (Vec<ValueSum>, Vec<Action<_>>) = acts.into_iter().unzip();
let rng = StdRng::from_seed(rng_seed);
Bundle::from_parts(
NonEmpty::from_vec(actions).unwrap(),
flags,
balances.into_iter().sum::<Result<ValueSum, _>>().unwrap(),
anchor,
Authorized {
proof: Proof::new(fake_proof),
binding_signature: sk.sign(rng, &fake_sighash),
}
)
}
}
}

View File

@ -1,102 +0,0 @@
//! Utility functions for computing bundle commitments
use blake2b_simd::{Hash as Blake2bHash, Params, State};
use std::io::Write;
use crate::bundle::{Authorization, Authorized, Bundle};
const ZCASH_ORCHARD_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrchardHash";
const ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActCHash";
const ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActMHash";
const ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActNHash";
const ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxAuthOrchaHash";
fn hasher(personal: &[u8; 16]) -> State {
Params::new().hash_length(32).personal(personal).to_state()
}
/// Write disjoint parts of each Orchard shielded action as 3 separate hashes:
/// * \[(nullifier, cmx, ephemeral_key, enc_ciphertext\[..52\])*\] personalized
/// with ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION
/// * \[enc_ciphertext\[52..564\]*\] (memo ciphertexts) personalized
/// with ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION
/// * \[(cv, rk, enc_ciphertext\[564..\], out_ciphertext)*\] personalized
/// with ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION
/// as defined in [ZIP-244: Transaction Identifier Non-Malleability][zip244]
///
/// Then, hash these together along with (flags, value_balance_orchard, anchor_orchard),
/// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION
///
/// [zip244]: https://zips.z.cash/zip-0244
pub fn hash_bundle_txid_data<'a, A: Authorization, V>(bundle: &'a Bundle<A, V>) -> Blake2bHash
where
i64: From<&'a V>,
{
let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION);
let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION);
let mut mh = hasher(ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION);
let mut nh = hasher(ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION);
for action in bundle.actions().iter() {
ch.write_all(&action.nullifier().to_bytes()).unwrap();
ch.write_all(&action.cmx().to_bytes()).unwrap();
ch.write_all(&action.encrypted_note().epk_bytes).unwrap();
ch.write_all(&action.encrypted_note().enc_ciphertext[..52])
.unwrap();
mh.write_all(&action.encrypted_note().enc_ciphertext[52..564])
.unwrap();
nh.write_all(&action.cv_net().to_bytes()).unwrap();
nh.write_all(&<[u8; 32]>::from(action.rk())).unwrap();
nh.write_all(&action.encrypted_note().enc_ciphertext[564..])
.unwrap();
nh.write_all(&action.encrypted_note().out_ciphertext)
.unwrap();
}
h.write_all(&ch.finalize().as_bytes()).unwrap();
h.write_all(&mh.finalize().as_bytes()).unwrap();
h.write_all(&nh.finalize().as_bytes()).unwrap();
h.write_all(&[bundle.flags().to_byte()]).unwrap();
h.write_all(&<i64>::from(bundle.value_balance()).to_le_bytes())
.unwrap();
h.write_all(&bundle.anchor().to_bytes()).unwrap();
h.finalize()
}
/// Construct the commitment for the absent bundle as defined in
/// [ZIP-244: Transaction Identifier Non-Malleability][zip244]
///
/// [zip244]: https://zips.z.cash/zip-0244
pub fn hash_bundle_txid_empty() -> Blake2bHash {
hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION).finalize()
}
/// Construct the commitment to the authorizing data of an
/// authorized bundle as defined in [ZIP-244: Transaction
/// Identifier Non-Malleability][zip244]
///
/// [zip244]: https://zips.z.cash/zip-0244
pub fn hash_bundle_auth_data<V>(bundle: &Bundle<Authorized, V>) -> Blake2bHash {
let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION);
h.write_all(bundle.authorization().proof().as_ref())
.unwrap();
for action in bundle.actions().iter() {
h.write_all(&<[u8; 64]>::from(action.authorization()))
.unwrap();
}
h.write_all(&<[u8; 64]>::from(
bundle.authorization().binding_signature(),
))
.unwrap();
h.finalize()
}
/// Construct the commitment for an absent bundle as defined in
/// [ZIP-244: Transaction Identifier Non-Malleability][zip244]
///
/// [zip244]: https://zips.z.cash/zip-0244
pub fn hash_bundle_auth_empty() -> Blake2bHash {
hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION).finalize()
}

File diff suppressed because it is too large Load Diff

View File

@ -1,867 +0,0 @@
use halo2::{
circuit::{AssignedCell, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
poly::Rotation,
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{
circuit::gadget::{
ecc::{chip::EccChip, X},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
CommitDomain, Message, MessagePiece,
},
utilities::{bitrange_subset, bool_check},
},
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P},
};
#[derive(Clone, Debug)]
pub struct CommitIvkConfig {
q_commit_ivk: Selector,
advices: [Column<Advice>; 10],
sinsemilla_config:
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
}
impl CommitIvkConfig {
pub(in crate::circuit) fn configure(
meta: &mut ConstraintSystem<pallas::Base>,
advices: [Column<Advice>; 10],
sinsemilla_config: SinsemillaConfig<
OrchardHashDomains,
OrchardCommitDomains,
OrchardFixedBases,
>,
) -> Self {
let q_commit_ivk = meta.selector();
let config = Self {
q_commit_ivk,
advices,
sinsemilla_config,
};
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
// We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits).
//
// a = bits 0..=249 of `ak`
// b = b_0||b_1||b_2`
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
// c = bits 5..=244 of `nk`
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
//
// `a`, `b`, `c`, `d` have been constrained by the Sinsemilla hash to be:
// - a: 250 bits,
// - b: 10 bits,
// - c: 240 bits,
// - d: 10 bits
//
/*
The pieces are laid out in this configuration:
| A_0 | A_1 | A_2 | A_3 | A_4 | A_5 | A_6 | A_7 | A_8 | q_commit_ivk |
-----------------------------------------------------------------------------------------------------
| ak | a | b | b_0 | b_1 | b_2 | z13_a | a_prime | z13_a_prime | 1 |
| nk | c | d | d_0 | d_1 | | z13_c | b2_c_prime| z14_b2_c_prime | 0 |
*/
meta.create_gate("CommitIvk canonicity check", |meta| {
let q_commit_ivk = meta.query_selector(config.q_commit_ivk);
// Useful constants
let two_pow_4 = pallas::Base::from(1 << 4);
let two_pow_5 = pallas::Base::from(1 << 5);
let two_pow_9 = two_pow_4 * two_pow_5;
let two_pow_250 = pallas::Base::from_u128(1 << 125).square();
let two_pow_254 = two_pow_250 * two_pow_4;
let ak = meta.query_advice(config.advices[0], Rotation::cur());
let nk = meta.query_advice(config.advices[0], Rotation::next());
// `a` is constrained by the Sinsemilla hash to be 250 bits.
let a = meta.query_advice(config.advices[1], Rotation::cur());
// `b` is constrained by the Sinsemilla hash to be 10 bits.
let b_whole = meta.query_advice(config.advices[2], Rotation::cur());
// `c` is constrained by the Sinsemilla hash to be 240 bits.
let c = meta.query_advice(config.advices[1], Rotation::next());
// `d` is constrained by the Sinsemilla hash to be 10 bits.
let d_whole = meta.query_advice(config.advices[2], Rotation::next());
// b = b_0||b_1||b_2`
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
//
// b_0 has been constrained outside this gate to be a four-bit value.
let b_0 = meta.query_advice(config.advices[3], Rotation::cur());
// This gate constrains b_1 to be a one-bit value.
let b_1 = meta.query_advice(config.advices[4], Rotation::cur());
// b_2 has been constrained outside this gate to be a five-bit value.
let b_2 = meta.query_advice(config.advices[5], Rotation::cur());
// Check that b_whole is consistent with the witnessed subpieces.
let b_decomposition_check =
b_whole - (b_0.clone() + b_1.clone() * two_pow_4 + b_2.clone() * two_pow_5);
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
//
// d_0 has been constrained outside this gate to be a nine-bit value.
let d_0 = meta.query_advice(config.advices[3], Rotation::next());
// This gate constrains d_1 to be a one-bit value.
let d_1 = meta.query_advice(config.advices[4], Rotation::next());
// Check that d_whole is consistent with the witnessed subpieces.
let d_decomposition_check = d_whole - (d_0.clone() + d_1.clone() * two_pow_9);
// Check `b_1` is a single-bit value
let b1_bool_check = bool_check(b_1.clone());
// Check `d_1` is a single-bit value
let d1_bool_check = bool_check(d_1.clone());
// Check that ak = a (250 bits) || b_0 (4 bits) || b_1 (1 bit)
let ak_decomposition_check =
a.clone() + b_0.clone() * two_pow_250 + b_1.clone() * two_pow_254 - ak;
// Check that nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
let nk_decomposition_check = {
let two_pow_245 = pallas::Base::from(1 << 49).pow(&[5, 0, 0, 0]);
b_2.clone()
+ c.clone() * two_pow_5
+ d_0.clone() * two_pow_245
+ d_1.clone() * two_pow_254
- nk
};
// ak = a (250 bits) || b_0 (4 bits) || b_1 (1 bit)
// The `ak` canonicity checks are enforced if and only if `b_1` = 1.
let ak_canonicity_checks = {
// b_1 = 1 => b_0 = 0
let b0_canon_check = b_1.clone() * b_0;
// z13_a is the 13th running sum output by the 10-bit Sinsemilla decomposition of `a`.
// b_1 = 1 => z13_a = 0
let z13_a_check = {
let z13_a = meta.query_advice(config.advices[6], Rotation::cur());
b_1.clone() * z13_a
};
// Check that a_prime = a + 2^130 - t_P.
// This is checked regardless of the value of b_1.
let a_prime_check = {
let a_prime = meta.query_advice(config.advices[7], Rotation::cur());
let two_pow_130 =
Expression::Constant(pallas::Base::from_u128(1 << 65).square());
let t_p = Expression::Constant(pallas::Base::from_u128(T_P));
a + two_pow_130 - t_p - a_prime
};
// Check that the running sum output by the 130-bit little-endian decomposition of
// `a_prime` is zero.
let z13_a_prime = {
let z13_a_prime = meta.query_advice(config.advices[8], Rotation::cur());
b_1 * z13_a_prime
};
std::iter::empty()
.chain(Some(("b0_canon_check", b0_canon_check)))
.chain(Some(("z13_a_check", z13_a_check)))
.chain(Some(("a_prime_check", a_prime_check)))
.chain(Some(("z13_a_prime", z13_a_prime)))
};
// nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
// The `nk` canonicity checks are enforced if and only if `d_1` = 1.
let nk_canonicity_checks = {
// d_1 = 1 => d_0 = 0
let c0_canon_check = d_1.clone() * d_0;
// d_1 = 1 => z13_c = 0, where z13_c is the 13th running sum
// output by the 10-bit Sinsemilla decomposition of `c`.
let z13_c_check = {
let z13_c = meta.query_advice(config.advices[6], Rotation::next());
d_1.clone() * z13_c
};
// Check that b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P.
// This is checked regardless of the value of d_1.
let b2_c_prime_check = {
let two_pow_5 = pallas::Base::from(1 << 5);
let two_pow_140 =
Expression::Constant(pallas::Base::from_u128(1 << 70).square());
let t_p = Expression::Constant(pallas::Base::from_u128(T_P));
let b2_c_prime = meta.query_advice(config.advices[7], Rotation::next());
b_2 + c * two_pow_5 + two_pow_140 - t_p - b2_c_prime
};
// Check that the running sum output by the 140-bit little-
// endian decomposition of b2_c_prime is zero.
let z14_b2_c_prime = {
let z14_b2_c_prime = meta.query_advice(config.advices[8], Rotation::next());
d_1 * z14_b2_c_prime
};
std::iter::empty()
.chain(Some(("c0_canon_check", c0_canon_check)))
.chain(Some(("z13_c_check", z13_c_check)))
.chain(Some(("b2_c_prime_check", b2_c_prime_check)))
.chain(Some(("z14_b2_c_prime", z14_b2_c_prime)))
};
std::iter::empty()
.chain(Some(("b1_bool_check", b1_bool_check)))
.chain(Some(("d1_bool_check", d1_bool_check)))
.chain(Some(("b_decomposition_check", b_decomposition_check)))
.chain(Some(("d_decomposition_check", d_decomposition_check)))
.chain(Some(("ak_decomposition_check", ak_decomposition_check)))
.chain(Some(("nk_decomposition_check", nk_decomposition_check)))
.chain(ak_canonicity_checks)
.chain(nk_canonicity_checks)
.map(move |(name, poly)| (name, q_commit_ivk.clone() * poly))
});
config
}
#[allow(non_snake_case)]
#[allow(clippy::type_complexity)]
pub(in crate::circuit) fn assign_region(
&self,
sinsemilla_chip: SinsemillaChip<
OrchardHashDomains,
OrchardCommitDomains,
OrchardFixedBases,
>,
ecc_chip: EccChip<OrchardFixedBases>,
mut layouter: impl Layouter<pallas::Base>,
ak: AssignedCell<pallas::Base, pallas::Base>,
nk: AssignedCell<pallas::Base, pallas::Base>,
rivk: Option<pallas::Scalar>,
) -> Result<X<pallas::Affine, EccChip<OrchardFixedBases>>, Error> {
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
// We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits).
//
// a = bits 0..=249 of `ak`
// b = b_0||b_1||b_2`
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
// c = bits 5..=244 of `nk`
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
// `a` = bits 0..=249 of `ak`
let a = {
let a = ak.value().map(|value| bitrange_subset(value, 0..250));
MessagePiece::from_field_elem(
sinsemilla_chip.clone(),
layouter.namespace(|| "a"),
a,
25,
)?
};
// `b = b_0||b_1||b_2`
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
let (b_0, b_1, b_2, b) = {
let b_0 = ak.value().map(|value| bitrange_subset(value, 250..254));
let b_1 = ak.value().map(|value| bitrange_subset(value, 254..255));
let b_2 = nk.value().map(|value| bitrange_subset(value, 0..5));
let b = b_0.zip(b_1).zip(b_2).map(|((b_0, b_1), b_2)| {
let b1_shifted = b_1 * pallas::Base::from(1 << 4);
let b2_shifted = b_2 * pallas::Base::from(1 << 5);
b_0 + b1_shifted + b2_shifted
});
// Constrain b_0 to be 4 bits.
let b_0 = self.sinsemilla_config.lookup_config().witness_short_check(
layouter.namespace(|| "b_0 is 4 bits"),
b_0,
4,
)?;
// Constrain b_2 to be 5 bits.
let b_2 = self.sinsemilla_config.lookup_config().witness_short_check(
layouter.namespace(|| "b_2 is 5 bits"),
b_2,
5,
)?;
// b_1 will be boolean-constrained in the custom gate.
let b = MessagePiece::from_field_elem(
sinsemilla_chip.clone(),
layouter.namespace(|| "b = b_0 || b_1 || b_2"),
b,
1,
)?;
(b_0, b_1, b_2, b)
};
// c = bits 5..=244 of `nk`
let c = {
let c = nk.value().map(|value| bitrange_subset(value, 5..245));
MessagePiece::from_field_elem(
sinsemilla_chip.clone(),
layouter.namespace(|| "c"),
c,
24,
)?
};
// `d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
let (d_0, d_1, d) = {
let d_0 = nk.value().map(|value| bitrange_subset(value, 245..254));
let d_1 = nk.value().map(|value| bitrange_subset(value, 254..255));
let d = d_0
.zip(d_1)
.map(|(d_0, d_1)| d_0 + d_1 * pallas::Base::from(1 << 9));
// Constrain d_0 to be 9 bits.
let d_0 = self.sinsemilla_config.lookup_config().witness_short_check(
layouter.namespace(|| "d_0 is 9 bits"),
d_0,
9,
)?;
// d_1 will be boolean-constrained in the custom gate.
let d = MessagePiece::from_field_elem(
sinsemilla_chip.clone(),
layouter.namespace(|| "d = d_0 || d_1"),
d,
1,
)?;
(d_0, d_1, d)
};
let (ivk, zs) = {
let message = Message::from_pieces(
sinsemilla_chip.clone(),
vec![a.clone(), b.clone(), c.clone(), d.clone()],
);
let domain =
CommitDomain::new(sinsemilla_chip, ecc_chip, &OrchardCommitDomains::CommitIvk);
domain.short_commit(layouter.namespace(|| "Hash ak||nk"), message, rivk)?
};
let z13_a = zs[0][13].clone();
let z13_c = zs[2][13].clone();
let (a_prime, z13_a_prime) = self.ak_canonicity(
layouter.namespace(|| "ak canonicity"),
a.inner().cell_value(),
)?;
let (b2_c_prime, z14_b2_c_prime) = self.nk_canonicity(
layouter.namespace(|| "nk canonicity"),
b_2.clone(),
c.inner().cell_value(),
)?;
let gate_cells = GateCells {
a: a.inner().cell_value(),
b: b.inner().cell_value(),
c: c.inner().cell_value(),
d: d.inner().cell_value(),
ak,
nk,
b_0,
b_1,
b_2,
d_0,
d_1,
z13_a,
a_prime,
z13_a_prime,
z13_c,
b2_c_prime,
z14_b2_c_prime,
};
self.assign_gate(
layouter.namespace(|| "Assign cells used in canonicity gate"),
gate_cells,
)?;
Ok(ivk)
}
#[allow(clippy::type_complexity)]
// Check canonicity of `ak` encoding
fn ak_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
a: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<
(
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
),
Error,
> {
// `ak` = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)`
// - b_1 = 1 => b_0 = 0
// - b_1 = 1 => a < t_P
// - (0 ≤ a < 2^130) => z13_a of SinsemillaHash(a) == 0
// - 0 ≤ a + 2^130 - t_P < 2^130 (thirteen 10-bit lookups)
// Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output
// the running sum at the end of it. If a_prime < 2^130, the running sum
// will be 0.
let a_prime = a.value().map(|a| {
let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square();
let t_p = pallas::Base::from_u128(T_P);
a + two_pow_130 - t_p
});
let zs = self.sinsemilla_config.lookup_config().witness_check(
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
a_prime,
13,
false,
)?;
let a_prime = zs[0].clone();
assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z13_a]
Ok((a_prime, zs[13].clone()))
}
#[allow(clippy::type_complexity)]
// Check canonicity of `nk` encoding
fn nk_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
b_2: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<
(
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
),
Error,
> {
// `nk` = `b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
// - d_1 = 1 => d_0 = 0
// - d_1 = 1 => b_2 + c * 2^5 < t_P
// - 0 ≤ b_2 + c * 2^5 < 2^140
// - b_2 was constrained to be 5 bits.
// - z_13 of SinsemillaHash(c) constrains bits 5..=134 to 130 bits
// - so b_2 + c * 2^5 is constrained to be 135 bits < 2^140.
// - 0 ≤ b_2 + c * 2^5 + 2^140 - t_P < 2^140 (14 ten-bit lookups)
// Decompose the low 140 bits of b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P, and output
// the running sum at the end of it. If b2_c_prime < 2^140, the running sum will be 0.
let b2_c_prime = b_2.value().zip(c.value()).map(|(b_2, c)| {
let two_pow_5 = pallas::Base::from(1 << 5);
let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square();
let t_p = pallas::Base::from_u128(T_P);
b_2 + c * two_pow_5 + two_pow_140 - t_p
});
let zs = self.sinsemilla_config.lookup_config().witness_check(
layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"),
b2_c_prime,
14,
false,
)?;
let b2_c_prime = zs[0].clone();
assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z14]
Ok((b2_c_prime, zs[14].clone()))
}
// Assign cells for the canonicity gate.
/*
The pieces are laid out in this configuration:
| A_0 | A_1 | A_2 | A_3 | A_4 | A_5 | A_6 | A_7 | A_8 | q_commit_ivk |
-----------------------------------------------------------------------------------------------------
| ak | a | b | b_0 | b_1 | b_2 | z13_a | a_prime | z13_a_prime | 1 |
| nk | c | d | d_0 | d_1 | | z13_c | b2_c_prime| z14_b2_c_prime | 0 |
*/
fn assign_gate(
&self,
mut layouter: impl Layouter<pallas::Base>,
gate_cells: GateCells,
) -> Result<(), Error> {
layouter.assign_region(
|| "Assign cells used in canonicity gate",
|mut region| {
// Enable selector on offset 0
self.q_commit_ivk.enable(&mut region, 0)?;
// Offset 0
{
let offset = 0;
// Copy in `ak`
gate_cells
.ak
.copy_advice(|| "ak", &mut region, self.advices[0], offset)?;
// Copy in `a`
gate_cells
.a
.copy_advice(|| "a", &mut region, self.advices[1], offset)?;
// Copy in `b`
gate_cells
.b
.copy_advice(|| "b", &mut region, self.advices[2], offset)?;
// Copy in `b_0`
gate_cells
.b_0
.copy_advice(|| "b_0", &mut region, self.advices[3], offset)?;
// Witness `b_1`
region.assign_advice(
|| "Witness b_1",
self.advices[4],
offset,
|| gate_cells.b_1.ok_or(Error::Synthesis),
)?;
// Copy in `b_2`
gate_cells
.b_2
.copy_advice(|| "b_2", &mut region, self.advices[5], offset)?;
// Copy in z13_a
gate_cells.z13_a.copy_advice(
|| "z13_a",
&mut region,
self.advices[6],
offset,
)?;
// Copy in a_prime
gate_cells.a_prime.copy_advice(
|| "a_prime",
&mut region,
self.advices[7],
offset,
)?;
// Copy in z13_a_prime
gate_cells.z13_a_prime.copy_advice(
|| "z13_a_prime",
&mut region,
self.advices[8],
offset,
)?;
}
// Offset 1
{
let offset = 1;
// Copy in `nk`
gate_cells
.nk
.copy_advice(|| "nk", &mut region, self.advices[0], offset)?;
// Copy in `c`
gate_cells
.c
.copy_advice(|| "c", &mut region, self.advices[1], offset)?;
// Copy in `d`
gate_cells
.d
.copy_advice(|| "d", &mut region, self.advices[2], offset)?;
// Copy in `d_0`
gate_cells
.d_0
.copy_advice(|| "d_0", &mut region, self.advices[3], offset)?;
// Witness `d_1`
region.assign_advice(
|| "Witness d_1",
self.advices[4],
offset,
|| gate_cells.d_1.ok_or(Error::Synthesis),
)?;
// Copy in z13_c
gate_cells.z13_c.copy_advice(
|| "z13_c",
&mut region,
self.advices[6],
offset,
)?;
// Copy in b2_c_prime
gate_cells.b2_c_prime.copy_advice(
|| "b2_c_prime",
&mut region,
self.advices[7],
offset,
)?;
// Copy in z14_b2_c_prime
gate_cells.z14_b2_c_prime.copy_advice(
|| "z14_b2_c_prime",
&mut region,
self.advices[8],
offset,
)?;
}
Ok(())
},
)
}
}
// Cells used in the canonicity gate.
struct GateCells {
a: AssignedCell<pallas::Base, pallas::Base>,
b: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
d: AssignedCell<pallas::Base, pallas::Base>,
ak: AssignedCell<pallas::Base, pallas::Base>,
nk: AssignedCell<pallas::Base, pallas::Base>,
b_0: AssignedCell<pallas::Base, pallas::Base>,
b_1: Option<pallas::Base>,
b_2: AssignedCell<pallas::Base, pallas::Base>,
d_0: AssignedCell<pallas::Base, pallas::Base>,
d_1: Option<pallas::Base>,
z13_a: AssignedCell<pallas::Base, pallas::Base>,
a_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_a_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_c: AssignedCell<pallas::Base, pallas::Base>,
b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
z14_b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
}
#[cfg(test)]
mod tests {
use super::CommitIvkConfig;
use crate::{
circuit::gadget::{
ecc::chip::{EccChip, EccConfig},
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{
fixed_bases::COMMIT_IVK_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
OrchardHashDomains, L_ORCHARD_BASE, T_Q,
},
primitives::sinsemilla::CommitDomain,
};
use group::ff::{Field, PrimeFieldBits};
use halo2::{
circuit::{AssignedCell, Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error},
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use rand::rngs::OsRng;
use std::convert::TryInto;
#[test]
fn commit_ivk() {
#[derive(Default)]
struct MyCircuit {
ak: Option<pallas::Base>,
nk: Option<pallas::Base>,
}
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl Circuit<pallas::Base> for MyCircuit {
type Config = (CommitIvkConfig, EccConfig<OrchardFixedBases>);
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
let advices = [
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
meta.advice_column(),
];
let constants = meta.fixed_column();
meta.enable_constant(constants);
for advice in advices.iter() {
meta.enable_equality(*advice);
}
let table_idx = meta.lookup_table_column();
let lookup = (
table_idx,
meta.lookup_table_column(),
meta.lookup_table_column(),
);
let lagrange_coeffs = [
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
meta.fixed_column(),
];
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
let sinsemilla_config = SinsemillaChip::<
OrchardHashDomains,
OrchardCommitDomains,
OrchardFixedBases,
>::configure(
meta,
advices[..5].try_into().unwrap(),
advices[2],
lagrange_coeffs[0],
lookup,
range_check,
);
let commit_ivk_config =
CommitIvkConfig::configure(meta, advices, sinsemilla_config);
let ecc_config = EccChip::<OrchardFixedBases>::configure(
meta,
advices,
lagrange_coeffs,
range_check,
);
(commit_ivk_config, ecc_config)
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
let (commit_ivk_config, ecc_config) = config;
// Load the Sinsemilla generator lookup table used by the whole circuit.
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(commit_ivk_config.sinsemilla_config.clone(), &mut layouter)?;
// Construct a Sinsemilla chip
let sinsemilla_chip =
SinsemillaChip::construct(commit_ivk_config.sinsemilla_config.clone());
// Construct an ECC chip
let ecc_chip = EccChip::construct(ecc_config);
// Witness ak
let ak = self.load_private(
layouter.namespace(|| "load ak"),
commit_ivk_config.advices[0],
self.ak,
)?;
// Witness nk
let nk = self.load_private(
layouter.namespace(|| "load nk"),
commit_ivk_config.advices[0],
self.nk,
)?;
// Use a random scalar for rivk
let rivk = pallas::Scalar::random(OsRng);
let ivk = commit_ivk_config.assign_region(
sinsemilla_chip,
ecc_chip,
layouter.namespace(|| "CommitIvk"),
ak,
nk,
Some(rivk),
)?;
let expected_ivk = {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
// Hash ak || nk
domain
.short_commit(
std::iter::empty()
.chain(
self.ak
.unwrap()
.to_le_bits()
.iter()
.by_val()
.take(L_ORCHARD_BASE),
)
.chain(
self.nk
.unwrap()
.to_le_bits()
.iter()
.by_val()
.take(L_ORCHARD_BASE),
),
&rivk,
)
.unwrap()
};
assert_eq!(&expected_ivk, ivk.inner().value().unwrap());
Ok(())
}
}
let two_pow_254 = pallas::Base::from_u128(1 << 127).square();
// Test different values of `ak`, `nk`
let circuits = [
// `ak` = 0, `nk` = 0
MyCircuit {
ak: Some(pallas::Base::zero()),
nk: Some(pallas::Base::zero()),
},
// `ak` = T_Q - 1, `nk` = T_Q - 1
MyCircuit {
ak: Some(pallas::Base::from_u128(T_Q - 1)),
nk: Some(pallas::Base::from_u128(T_Q - 1)),
},
// `ak` = T_Q, `nk` = T_Q
MyCircuit {
ak: Some(pallas::Base::from_u128(T_Q)),
nk: Some(pallas::Base::from_u128(T_Q)),
},
// `ak` = 2^127 - 1, `nk` = 2^127 - 1
MyCircuit {
ak: Some(pallas::Base::from_u128((1 << 127) - 1)),
nk: Some(pallas::Base::from_u128((1 << 127) - 1)),
},
// `ak` = 2^127, `nk` = 2^127
MyCircuit {
ak: Some(pallas::Base::from_u128(1 << 127)),
nk: Some(pallas::Base::from_u128(1 << 127)),
},
// `ak` = 2^254 - 1, `nk` = 2^254 - 1
MyCircuit {
ak: Some(two_pow_254 - pallas::Base::one()),
nk: Some(two_pow_254 - pallas::Base::one()),
},
// `ak` = 2^254, `nk` = 2^254
MyCircuit {
ak: Some(two_pow_254),
nk: Some(two_pow_254),
},
];
for circuit in circuits.iter() {
let prover = MockProver::<pallas::Base>::run(11, circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()));
}
}
}

View File

@ -1,47 +1,6 @@
//! Gadgets used in the Orchard circuit.
use pasta_curves::pallas;
use crate::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
use ecc::chip::EccChip;
use poseidon::Pow5Chip as PoseidonChip;
use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip};
pub(crate) mod ecc;
pub mod poseidon;
pub(crate) mod sinsemilla;
pub mod utilities;
impl super::Config {
pub(super) fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
EccChip::construct(self.ecc_config.clone())
}
pub(super) fn sinsemilla_chip_1(
&self,
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
SinsemillaChip::construct(self.sinsemilla_config_1.clone())
}
pub(super) fn sinsemilla_chip_2(
&self,
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
SinsemillaChip::construct(self.sinsemilla_config_2.clone())
}
pub(super) fn merkle_chip_1(
&self,
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
MerkleChip::construct(self.merkle_config_1.clone())
}
pub(super) fn merkle_chip_2(
&self,
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
MerkleChip::construct(self.merkle_config_2.clone())
}
pub(super) fn poseidon_chip(&self) -> PoseidonChip<pallas::Base, 3, 2> {
PoseidonChip::construct(self.poseidon_config.clone())
}
}

View File

@ -498,7 +498,6 @@ mod tests {
use super::chip::{EccChip, EccConfig};
use crate::circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig;
use crate::constants::OrchardFixedBases;
struct MyCircuit {
test_errors: bool,

View File

@ -480,7 +480,6 @@ pub mod tests {
},
utilities::UtilitiesInstructions,
};
use crate::constants::OrchardFixedBases;
pub fn test_mul(
chip: EccChip<OrchardFixedBases>,

View File

@ -393,7 +393,6 @@ pub mod tests {
},
utilities::UtilitiesInstructions,
};
use crate::constants::{NullifierK, OrchardFixedBases};
pub fn test_mul_fixed_base_field(
chip: EccChip<OrchardFixedBases>,

View File

@ -185,7 +185,6 @@ pub mod tests {
chip::{EccChip, FixedPoint as _, H},
FixedPoint, NonIdentityPoint, Point,
};
use crate::constants::{OrchardFixedBases, OrchardFixedBasesFull};
pub fn test_mul_fixed(
chip: EccChip<OrchardFixedBases>,

View File

@ -244,7 +244,6 @@ pub mod tests {
},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
};
use crate::constants::{OrchardFixedBases, ValueCommitV};
#[allow(clippy::op_ref)]
pub fn test_mul_fixed_short(

View File

@ -416,6 +416,7 @@ mod tests {
use super::{
chip::{SinsemillaChip, SinsemillaConfig},
merkle::MERKLE_CRH_PERSONALIZATION,
CommitDomain, HashDomain, Message, MessagePiece,
};
@ -427,10 +428,6 @@ mod tests {
},
utilities::lookup_range_check::LookupRangeCheckConfig,
},
constants::{
fixed_bases::COMMIT_IVK_PERSONALIZATION, sinsemilla::MERKLE_CRH_PERSONALIZATION,
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
},
primitives::sinsemilla::{self, K},
};

View File

@ -136,14 +136,9 @@ pub mod tests {
MerklePath,
};
use crate::{
circuit::gadget::{
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains},
note::commitment::ExtractedNoteCommitment,
tree,
use crate::circuit::gadget::{
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
};
use group::ff::{Field, PrimeField};

View File

@ -346,7 +346,7 @@ where
#[cfg(test)]
{
use super::MERKLE_CRH_PERSONALIZATION;
use crate::{primitives::sinsemilla::HashDomain, spec::i2lebsp};
use crate::{circuit::gadget::utilities::i2lebsp, primitives::sinsemilla::HashDomain};
use group::ff::PrimeFieldBits;
if let (Some(left), Some(right)) = (left.value(), right.value()) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +0,0 @@
//! Constants used in the Orchard protocol.
pub mod fixed_bases;
pub mod sinsemilla;
pub mod util;
pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, H};
pub use sinsemilla::{OrchardCommitDomains, OrchardHashDomains};
/// $\mathsf{MerkleDepth^{Orchard}}$
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
/// <https://github.com/zcash/pasta>
pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
/// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$.
/// <https://github.com/zcash/pasta>
pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
pub(crate) const L_ORCHARD_BASE: usize = 255;
/// $\ell^\mathsf{Orchard}_\mathsf{scalar}$
pub(crate) const L_ORCHARD_SCALAR: usize = 255;
/// $\ell_\mathsf{value}$
pub(crate) const L_VALUE: usize = 64;
/// SWU hash-to-curve personalization for the group hash for key diversification
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &str = "z.cash:Orchard-gd";
#[cfg(test)]
mod tests {
use ff::PrimeField;
use pasta_curves::{arithmetic::FieldExt, pallas};
#[test]
// Orchard uses the Pallas base field as its base field.
fn l_orchard_base() {
assert_eq!(super::L_ORCHARD_BASE, pallas::Base::NUM_BITS as usize);
}
#[test]
// Orchard uses the Pallas base field as its base field.
fn l_orchard_scalar() {
assert_eq!(super::L_ORCHARD_SCALAR, pallas::Scalar::NUM_BITS as usize);
}
#[test]
fn t_q() {
let t_q = pallas::Scalar::from_u128(super::T_Q);
let two_pow_254 = pallas::Scalar::from_u128(1 << 127).square();
assert_eq!(t_q + two_pow_254, pallas::Scalar::zero());
}
#[test]
fn t_p() {
let t_p = pallas::Base::from_u128(super::T_P);
let two_pow_254 = pallas::Base::from_u128(1 << 127).square();
assert_eq!(t_p + two_pow_254, pallas::Base::zero());
}
}

View File

@ -1,161 +0,0 @@
//! Orchard fixed bases.
use super::{L_ORCHARD_SCALAR, L_VALUE};
use crate::circuit::gadget::ecc::{
chip::{BaseFieldElem, FixedPoint, FullScalar, ShortScalar},
FixedPoints,
};
use pasta_curves::pallas;
pub mod commit_ivk_r;
pub mod note_commit_r;
pub mod nullifier_k;
pub mod spend_auth_g;
pub mod value_commit_r;
pub mod value_commit_v;
/// SWU hash-to-curve personalization for the spending key base point and
/// the nullifier base point K^Orchard
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
/// SWU hash-to-curve personalization for the value commitment generator
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
/// SWU hash-to-curve value for the value commitment generator
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
/// SWU hash-to-curve value for the value commitment generator
pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r";
/// SWU hash-to-curve personalization for the note commitment generator
pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit";
/// SWU hash-to-curve personalization for the IVK commitment generator
pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk";
/// Window size for fixed-base scalar multiplication
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
/// $2^{`FIXED_BASE_WINDOW_SIZE`}$
pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE;
/// Number of windows for a full-width scalar
pub const NUM_WINDOWS: usize =
(L_ORCHARD_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
/// Number of windows for a short signed scalar
pub const NUM_WINDOWS_SHORT: usize =
(L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
// A sum type for both full-width and short bases. This enables us to use the
// shared functionality of full-width and short fixed-base scalar multiplication.
pub enum OrchardFixedBases {
Full(OrchardFixedBasesFull),
NullifierK,
ValueCommitV,
}
impl From<OrchardFixedBasesFull> for OrchardFixedBases {
fn from(full_width_base: OrchardFixedBasesFull) -> Self {
Self::Full(full_width_base)
}
}
impl From<ValueCommitV> for OrchardFixedBases {
fn from(_value_commit_v: ValueCommitV) -> Self {
Self::ValueCommitV
}
}
impl From<NullifierK> for OrchardFixedBases {
fn from(_nullifier_k: NullifierK) -> Self {
Self::NullifierK
}
}
/// The Orchard fixed bases used in scalar mul with full-width scalars.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OrchardFixedBasesFull {
CommitIvkR,
NoteCommitR,
ValueCommitR,
SpendAuthG,
}
/// NullifierK is used in scalar mul with a base field element.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct NullifierK;
/// ValueCommitV is used in scalar mul with a short signed scalar.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ValueCommitV;
impl FixedPoints<pallas::Affine> for OrchardFixedBases {
type FullScalar = OrchardFixedBasesFull;
type Base = NullifierK;
type ShortScalar = ValueCommitV;
}
impl FixedPoint<pallas::Affine> for OrchardFixedBasesFull {
type ScalarKind = FullScalar;
fn generator(&self) -> pallas::Affine {
match self {
Self::CommitIvkR => commit_ivk_r::generator(),
Self::NoteCommitR => note_commit_r::generator(),
Self::ValueCommitR => value_commit_r::generator(),
Self::SpendAuthG => spend_auth_g::generator(),
}
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
match self {
Self::CommitIvkR => commit_ivk_r::U.to_vec(),
Self::NoteCommitR => note_commit_r::U.to_vec(),
Self::ValueCommitR => value_commit_r::U.to_vec(),
Self::SpendAuthG => spend_auth_g::U.to_vec(),
}
}
fn z(&self) -> Vec<u64> {
match self {
Self::CommitIvkR => commit_ivk_r::Z.to_vec(),
Self::NoteCommitR => note_commit_r::Z.to_vec(),
Self::ValueCommitR => value_commit_r::Z.to_vec(),
Self::SpendAuthG => spend_auth_g::Z.to_vec(),
}
}
}
impl FixedPoint<pallas::Affine> for NullifierK {
type ScalarKind = BaseFieldElem;
fn generator(&self) -> pallas::Affine {
nullifier_k::generator()
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
nullifier_k::U.to_vec()
}
fn z(&self) -> Vec<u64> {
nullifier_k::Z.to_vec()
}
}
impl FixedPoint<pallas::Affine> for ValueCommitV {
type ScalarKind = ShortScalar;
fn generator(&self) -> pallas::Affine {
value_commit_v::generator()
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
value_commit_v::U_SHORT.to_vec()
}
fn z(&self) -> Vec<u64> {
value_commit_v::Z_SHORT.to_vec()
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,815 +0,0 @@
use group::ff::PrimeField;
use pasta_curves::{arithmetic::CurveAffine, pallas};
/// The value commitment is used to check balance between inputs and outputs. The value is
/// placed over this generator.
pub const GENERATOR: ([u8; 32], [u8; 32]) = (
[
103, 67, 249, 58, 110, 189, 167, 42, 140, 124, 90, 43, 127, 163, 4, 254, 50, 178, 155, 79,
112, 106, 168, 247, 66, 15, 61, 142, 122, 89, 112, 47,
],
[
142, 242, 90, 175, 126, 196, 19, 164, 219, 227, 255, 167, 102, 167, 158, 29, 66, 108, 109,
19, 99, 127, 145, 30, 175, 25, 25, 49, 105, 81, 14, 45,
],
);
/// Short signed z-values for GENERATOR
pub const Z_SHORT: [u64; super::NUM_WINDOWS_SHORT] = [
163547, 76040, 88852, 128479, 54088, 89871, 39598, 144309, 43471, 102492, 741, 55288, 33756,
77312, 12095, 48253, 45718, 202901, 33132, 71081, 152108, 169712,
];
/// Short signed u-values for GENERATOR
pub const U_SHORT: [[[u8; 32]; super::H]; super::NUM_WINDOWS_SHORT] = [
[
[
16, 88, 158, 52, 92, 165, 59, 177, 224, 70, 108, 93, 144, 51, 8, 133, 114, 192, 151,
40, 85, 33, 52, 118, 147, 163, 220, 193, 171, 43, 73, 27,
],
[
201, 84, 190, 165, 219, 228, 63, 155, 182, 74, 179, 74, 230, 96, 89, 168, 110, 162, 83,
218, 185, 137, 194, 92, 244, 85, 1, 60, 59, 16, 119, 14,
],
[
113, 146, 25, 32, 206, 37, 173, 225, 58, 41, 184, 214, 177, 123, 211, 55, 42, 208, 214,
45, 149, 99, 31, 171, 172, 226, 219, 171, 246, 85, 116, 59,
],
[
10, 79, 89, 241, 37, 161, 115, 213, 183, 43, 68, 233, 219, 249, 131, 137, 41, 119, 153,
24, 92, 87, 138, 167, 93, 46, 32, 97, 102, 164, 164, 53,
],
[
50, 252, 2, 254, 234, 133, 190, 34, 244, 192, 31, 96, 156, 152, 150, 88, 154, 224, 223,
144, 223, 113, 203, 226, 214, 44, 69, 198, 171, 60, 26, 45,
],
[
151, 65, 173, 0, 249, 13, 78, 201, 84, 58, 255, 120, 124, 169, 98, 242, 239, 132, 149,
204, 146, 244, 55, 176, 56, 26, 19, 148, 125, 42, 26, 32,
],
[
6, 29, 196, 176, 135, 157, 248, 239, 196, 9, 100, 82, 220, 41, 203, 49, 35, 108, 76,
59, 222, 47, 82, 39, 35, 23, 88, 136, 61, 90, 23, 57,
],
[
169, 203, 101, 116, 220, 68, 34, 248, 199, 107, 202, 59, 111, 171, 90, 121, 119, 80,
146, 214, 191, 197, 220, 212, 125, 139, 12, 140, 198, 191, 110, 31,
],
],
[
[
16, 171, 139, 236, 248, 28, 27, 145, 109, 161, 63, 121, 19, 190, 106, 193, 13, 122,
108, 54, 95, 202, 195, 184, 118, 60, 72, 213, 154, 240, 52, 34,
],
[
131, 218, 16, 142, 97, 153, 190, 249, 118, 171, 187, 254, 21, 17, 202, 48, 107, 23,
103, 72, 159, 238, 228, 38, 183, 166, 74, 136, 77, 51, 85, 18,
],
[
137, 78, 204, 243, 154, 87, 148, 200, 11, 98, 58, 33, 89, 170, 198, 87, 29, 187, 167,
122, 157, 151, 5, 148, 254, 216, 113, 12, 158, 243, 102, 63,
],
[
16, 187, 216, 98, 244, 19, 70, 209, 217, 133, 240, 226, 111, 219, 247, 74, 4, 231, 183,
216, 35, 174, 192, 96, 129, 159, 39, 96, 200, 174, 126, 1,
],
[
185, 2, 174, 129, 110, 163, 212, 152, 214, 139, 105, 150, 23, 209, 185, 245, 71, 8,
171, 142, 43, 57, 167, 172, 233, 77, 194, 166, 118, 232, 127, 35,
],
[
57, 28, 236, 33, 234, 0, 214, 131, 161, 209, 42, 25, 194, 150, 82, 126, 216, 23, 81,
85, 90, 47, 95, 46, 119, 108, 253, 12, 92, 29, 13, 25,
],
[
100, 162, 156, 175, 254, 203, 69, 171, 182, 190, 85, 161, 94, 117, 225, 0, 95, 121,
190, 235, 79, 7, 147, 134, 48, 156, 113, 55, 114, 229, 174, 25,
],
[
243, 93, 223, 98, 132, 0, 171, 125, 135, 53, 111, 49, 49, 173, 69, 211, 62, 106, 168,
221, 115, 208, 2, 20, 14, 150, 124, 130, 189, 48, 16, 15,
],
],
[
[
43, 238, 4, 240, 186, 135, 47, 128, 185, 51, 70, 196, 3, 137, 22, 254, 29, 247, 161,
233, 187, 255, 239, 183, 150, 179, 8, 64, 90, 33, 17, 54,
],
[
20, 226, 19, 60, 92, 195, 1, 255, 100, 188, 89, 54, 98, 225, 138, 133, 26, 140, 202,
76, 107, 199, 46, 54, 167, 23, 119, 80, 19, 25, 34, 5,
],
[
145, 109, 10, 159, 200, 145, 32, 253, 32, 93, 187, 12, 66, 161, 44, 217, 16, 100, 225,
37, 139, 125, 169, 119, 83, 34, 96, 130, 234, 255, 138, 38,
],
[
148, 127, 71, 227, 195, 244, 12, 49, 83, 78, 218, 9, 176, 158, 12, 5, 194, 54, 44, 157,
252, 228, 175, 170, 54, 91, 49, 254, 83, 228, 180, 61,
],
[
36, 96, 238, 61, 227, 144, 153, 81, 121, 197, 74, 190, 35, 216, 255, 92, 70, 7, 168,
219, 130, 255, 172, 71, 200, 31, 142, 232, 255, 117, 96, 15,
],
[
50, 152, 255, 238, 188, 127, 140, 240, 203, 33, 246, 193, 228, 179, 25, 1, 227, 194,
79, 70, 41, 160, 83, 243, 148, 1, 95, 86, 63, 22, 55, 40,
],
[
99, 228, 147, 46, 232, 157, 225, 210, 45, 159, 169, 42, 184, 90, 3, 165, 62, 4, 93,
181, 74, 187, 112, 156, 26, 103, 199, 84, 132, 148, 14, 24,
],
[
120, 163, 182, 125, 197, 141, 122, 21, 166, 103, 203, 57, 160, 228, 242, 192, 203, 40,
233, 179, 8, 173, 199, 21, 213, 215, 222, 129, 23, 153, 25, 29,
],
],
[
[
189, 6, 27, 46, 68, 6, 124, 69, 159, 212, 18, 104, 218, 16, 66, 183, 250, 92, 69, 5,
218, 234, 31, 198, 123, 100, 216, 103, 106, 113, 192, 43,
],
[
226, 152, 83, 159, 143, 155, 164, 90, 171, 100, 22, 240, 187, 26, 55, 172, 170, 133,
128, 192, 29, 33, 35, 8, 59, 142, 20, 204, 151, 172, 29, 28,
],
[
196, 57, 232, 112, 46, 200, 26, 93, 224, 191, 117, 154, 9, 47, 228, 145, 122, 137, 238,
236, 70, 14, 112, 163, 96, 140, 239, 122, 75, 200, 206, 12,
],
[
91, 66, 65, 159, 86, 50, 113, 39, 30, 12, 213, 235, 15, 70, 163, 119, 112, 27, 22, 170,
151, 20, 71, 172, 65, 251, 224, 76, 26, 189, 118, 42,
],
[
195, 200, 87, 207, 125, 122, 254, 50, 24, 40, 189, 169, 237, 228, 134, 66, 37, 220, 23,
98, 202, 193, 152, 184, 171, 63, 105, 11, 70, 94, 154, 58,
],
[
4, 0, 82, 125, 41, 130, 11, 238, 112, 201, 95, 30, 144, 31, 31, 233, 86, 121, 145, 200,
204, 89, 182, 51, 151, 169, 58, 206, 184, 219, 181, 46,
],
[
36, 240, 60, 205, 120, 239, 9, 98, 61, 228, 247, 9, 145, 71, 67, 10, 164, 160, 186,
104, 25, 225, 210, 37, 31, 42, 58, 208, 44, 19, 30, 29,
],
[
163, 214, 140, 64, 48, 239, 54, 175, 108, 32, 187, 248, 142, 77, 244, 117, 10, 236, 39,
80, 158, 44, 98, 44, 248, 24, 208, 60, 173, 247, 115, 28,
],
],
[
[
230, 166, 7, 118, 178, 55, 26, 250, 91, 159, 230, 158, 34, 135, 91, 49, 166, 244, 124,
175, 86, 228, 159, 119, 231, 15, 19, 199, 219, 85, 233, 19,
],
[
65, 240, 77, 9, 197, 255, 96, 35, 239, 161, 177, 115, 83, 114, 180, 179, 118, 158, 220,
61, 177, 3, 175, 70, 132, 51, 87, 79, 36, 104, 129, 15,
],
[
245, 9, 141, 218, 228, 57, 204, 250, 83, 156, 160, 158, 146, 157, 206, 198, 16, 94, 80,
59, 31, 242, 163, 30, 80, 99, 32, 142, 193, 101, 172, 22,
],
[
22, 68, 73, 27, 236, 149, 225, 78, 69, 83, 174, 202, 98, 113, 148, 167, 43, 132, 118,
49, 153, 96, 186, 134, 20, 103, 123, 48, 75, 104, 207, 6,
],
[
186, 5, 66, 183, 166, 255, 222, 14, 34, 0, 105, 167, 36, 11, 210, 228, 91, 103, 65,
248, 3, 48, 117, 240, 180, 214, 201, 222, 5, 202, 103, 42,
],
[
231, 31, 19, 122, 240, 72, 34, 93, 204, 125, 90, 215, 124, 174, 124, 28, 145, 103, 126,
178, 1, 9, 152, 240, 251, 118, 14, 195, 197, 167, 136, 22,
],
[
121, 57, 39, 104, 26, 135, 98, 221, 85, 199, 94, 230, 223, 129, 28, 191, 185, 0, 46,
200, 72, 116, 202, 255, 80, 180, 13, 98, 229, 217, 238, 17,
],
[
228, 158, 59, 241, 50, 224, 177, 78, 8, 121, 211, 157, 95, 196, 88, 59, 85, 141, 134,
50, 123, 168, 175, 115, 240, 153, 223, 61, 71, 229, 77, 10,
],
],
[
[
196, 151, 212, 168, 230, 19, 214, 179, 118, 17, 196, 50, 76, 231, 114, 80, 178, 137,
232, 241, 60, 2, 0, 124, 25, 239, 98, 131, 220, 159, 221, 51,
],
[
207, 216, 50, 53, 106, 200, 85, 166, 137, 33, 29, 239, 97, 46, 104, 218, 177, 150, 178,
64, 232, 238, 208, 98, 103, 58, 233, 97, 1, 243, 73, 5,
],
[
174, 215, 22, 124, 193, 136, 79, 91, 111, 146, 51, 128, 249, 129, 203, 168, 157, 164,
252, 144, 206, 10, 63, 253, 250, 113, 251, 65, 218, 23, 12, 25,
],
[
186, 92, 109, 134, 176, 198, 214, 156, 148, 7, 106, 121, 226, 70, 250, 192, 180, 164,
16, 29, 245, 196, 155, 183, 214, 26, 3, 26, 249, 134, 153, 38,
],
[
244, 56, 170, 227, 236, 145, 153, 150, 166, 77, 201, 48, 100, 130, 204, 194, 108, 207,
3, 243, 227, 41, 45, 8, 136, 106, 58, 70, 126, 3, 132, 59,
],
[
215, 169, 24, 118, 5, 192, 171, 217, 47, 86, 212, 249, 228, 92, 248, 103, 242, 222, 17,
206, 36, 157, 126, 224, 105, 113, 239, 102, 54, 25, 151, 14,
],
[
129, 213, 202, 163, 74, 201, 249, 43, 200, 62, 4, 232, 68, 211, 44, 153, 25, 71, 155,
5, 93, 19, 117, 195, 201, 179, 158, 207, 252, 213, 166, 62,
],
[
86, 75, 236, 222, 217, 118, 49, 0, 141, 130, 201, 248, 34, 225, 69, 91, 186, 90, 246,
36, 141, 152, 166, 216, 118, 7, 38, 133, 147, 190, 79, 3,
],
],
[
[
226, 55, 121, 73, 104, 77, 129, 202, 168, 123, 192, 163, 28, 9, 195, 37, 116, 207, 125,
198, 203, 244, 121, 236, 232, 91, 168, 14, 142, 241, 250, 60,
],
[
222, 86, 183, 37, 132, 107, 210, 125, 127, 46, 94, 81, 18, 91, 72, 160, 16, 193, 239,
114, 238, 186, 186, 203, 96, 169, 87, 155, 108, 36, 97, 6,
],
[
110, 110, 99, 98, 167, 78, 75, 128, 33, 138, 18, 19, 194, 192, 219, 184, 74, 196, 82,
115, 241, 102, 30, 197, 199, 194, 154, 120, 49, 10, 95, 37,
],
[
212, 206, 154, 98, 20, 33, 185, 182, 138, 207, 65, 197, 246, 19, 132, 52, 173, 186, 42,
243, 88, 20, 51, 11, 206, 25, 216, 48, 162, 138, 124, 13,
],
[
32, 161, 64, 72, 1, 32, 243, 175, 251, 37, 86, 248, 136, 187, 181, 55, 39, 255, 98,
228, 189, 235, 194, 2, 228, 39, 92, 104, 245, 17, 117, 7,
],
[
207, 205, 116, 251, 54, 21, 8, 82, 173, 45, 205, 38, 245, 155, 16, 56, 198, 232, 173,
88, 97, 22, 234, 26, 139, 206, 108, 254, 123, 87, 181, 26,
],
[
38, 147, 223, 169, 68, 76, 49, 169, 137, 141, 72, 63, 166, 88, 34, 220, 163, 91, 167,
251, 29, 160, 254, 199, 205, 74, 158, 105, 252, 182, 158, 21,
],
[
221, 44, 183, 72, 226, 191, 226, 165, 162, 153, 186, 190, 97, 53, 19, 115, 215, 71,
155, 33, 79, 120, 197, 228, 216, 212, 249, 15, 179, 11, 216, 32,
],
],
[
[
248, 136, 25, 30, 234, 18, 242, 209, 97, 211, 74, 228, 236, 199, 101, 200, 206, 52,
146, 207, 72, 125, 28, 3, 60, 86, 34, 195, 250, 251, 204, 0,
],
[
204, 182, 197, 171, 247, 159, 161, 27, 18, 146, 249, 99, 198, 138, 25, 61, 119, 232,
160, 152, 18, 149, 7, 67, 125, 231, 237, 3, 68, 190, 137, 0,
],
[
141, 245, 108, 181, 49, 171, 106, 247, 202, 169, 106, 39, 93, 40, 122, 2, 236, 255,
198, 215, 122, 254, 242, 192, 49, 250, 243, 35, 7, 219, 21, 22,
],
[
239, 85, 174, 15, 207, 84, 128, 92, 87, 80, 129, 20, 21, 225, 233, 158, 193, 136, 141,
114, 66, 146, 29, 193, 223, 250, 27, 56, 195, 15, 135, 17,
],
[
231, 242, 76, 43, 57, 10, 41, 166, 32, 254, 129, 47, 147, 118, 189, 200, 44, 102, 204,
116, 96, 82, 186, 150, 106, 27, 30, 73, 237, 94, 36, 44,
],
[
240, 139, 69, 197, 199, 228, 206, 96, 255, 229, 189, 207, 65, 97, 93, 211, 161, 190,
228, 249, 50, 82, 223, 251, 13, 173, 241, 221, 78, 243, 105, 19,
],
[
44, 224, 170, 161, 50, 93, 212, 80, 100, 243, 51, 74, 51, 165, 60, 208, 244, 18, 158,
30, 158, 81, 111, 213, 136, 95, 125, 173, 143, 108, 106, 4,
],
[
134, 244, 131, 92, 152, 118, 30, 139, 153, 128, 62, 115, 88, 25, 58, 29, 205, 101, 47,
208, 93, 89, 222, 17, 122, 112, 71, 56, 147, 68, 92, 22,
],
],
[
[
59, 157, 112, 130, 217, 2, 102, 228, 79, 211, 152, 82, 183, 186, 47, 151, 125, 13, 97,
121, 115, 253, 17, 121, 227, 250, 99, 14, 84, 249, 18, 30,
],
[
80, 180, 155, 59, 6, 182, 136, 39, 134, 168, 238, 138, 129, 174, 195, 206, 210, 167,
214, 167, 35, 139, 130, 27, 21, 59, 7, 200, 165, 37, 91, 29,
],
[
220, 228, 189, 172, 68, 102, 135, 236, 7, 70, 152, 244, 120, 217, 67, 44, 43, 74, 155,
179, 2, 148, 106, 238, 232, 186, 181, 130, 141, 114, 60, 1,
],
[
68, 132, 80, 55, 28, 52, 222, 165, 156, 6, 214, 236, 207, 37, 223, 118, 42, 55, 40,
123, 208, 181, 240, 56, 14, 142, 58, 72, 193, 71, 120, 58,
],
[
93, 114, 68, 232, 179, 37, 202, 74, 41, 64, 245, 112, 233, 162, 231, 19, 223, 207, 232,
213, 178, 60, 106, 26, 35, 191, 108, 19, 243, 220, 40, 41,
],
[
166, 223, 96, 196, 120, 210, 67, 47, 249, 123, 164, 213, 148, 138, 7, 155, 96, 222,
176, 166, 88, 85, 95, 71, 221, 237, 138, 181, 198, 165, 163, 0,
],
[
241, 254, 24, 83, 47, 65, 146, 151, 5, 182, 233, 205, 182, 13, 75, 173, 10, 14, 48,
223, 227, 201, 141, 212, 114, 205, 196, 92, 137, 253, 127, 60,
],
[
20, 41, 204, 77, 168, 230, 68, 202, 73, 251, 254, 88, 95, 80, 130, 216, 122, 75, 173,
105, 236, 192, 177, 209, 26, 66, 205, 127, 154, 188, 245, 17,
],
],
[
[
239, 194, 214, 218, 225, 244, 0, 110, 12, 75, 130, 236, 76, 102, 205, 64, 104, 144,
198, 188, 183, 46, 119, 96, 230, 68, 210, 161, 253, 91, 8, 20,
],
[
84, 32, 226, 77, 213, 16, 207, 156, 234, 224, 147, 173, 186, 249, 186, 155, 90, 255,
34, 55, 48, 108, 76, 214, 254, 66, 95, 200, 174, 191, 52, 43,
],
[
37, 103, 206, 174, 250, 172, 136, 87, 30, 68, 89, 230, 110, 190, 148, 71, 5, 249, 217,
112, 54, 182, 127, 54, 173, 89, 6, 63, 230, 69, 32, 35,
],
[
160, 186, 242, 212, 179, 197, 16, 239, 56, 24, 91, 241, 68, 7, 138, 200, 93, 194, 45,
155, 210, 60, 30, 4, 167, 246, 82, 244, 71, 217, 31, 20,
],
[
182, 132, 62, 134, 4, 186, 95, 160, 230, 255, 125, 156, 5, 134, 66, 99, 83, 182, 156,
207, 98, 84, 197, 48, 160, 47, 126, 2, 253, 64, 69, 25,
],
[
135, 241, 60, 121, 32, 218, 195, 61, 68, 66, 190, 195, 208, 2, 201, 111, 158, 101, 108,
228, 145, 141, 82, 80, 36, 16, 157, 212, 65, 213, 188, 61,
],
[
190, 186, 202, 30, 121, 177, 200, 82, 245, 162, 14, 253, 114, 50, 43, 134, 246, 12,
100, 222, 149, 242, 117, 174, 136, 192, 117, 132, 228, 144, 238, 39,
],
[
160, 120, 19, 13, 34, 38, 71, 236, 116, 162, 150, 254, 247, 252, 222, 198, 196, 59, 98,
165, 54, 33, 22, 120, 58, 73, 225, 42, 37, 211, 88, 21,
],
],
[
[
252, 1, 229, 131, 50, 189, 111, 31, 191, 210, 177, 219, 234, 21, 100, 182, 115, 212,
154, 111, 130, 59, 237, 32, 142, 202, 110, 96, 166, 120, 188, 1,
],
[
247, 244, 137, 120, 38, 62, 94, 38, 17, 38, 102, 240, 225, 129, 15, 214, 213, 142, 79,
176, 156, 118, 85, 80, 167, 47, 122, 152, 206, 19, 67, 40,
],
[
27, 159, 102, 201, 17, 4, 75, 28, 159, 5, 194, 6, 63, 104, 157, 219, 53, 38, 84, 216,
73, 181, 11, 118, 29, 177, 147, 135, 150, 5, 58, 10,
],
[
97, 168, 102, 245, 40, 187, 155, 99, 147, 65, 114, 119, 191, 225, 196, 34, 117, 134,
116, 162, 73, 69, 158, 103, 144, 16, 22, 216, 146, 38, 10, 41,
],
[
149, 231, 10, 10, 17, 16, 88, 231, 24, 215, 115, 237, 123, 68, 9, 209, 24, 141, 150,
207, 109, 56, 107, 192, 252, 112, 156, 0, 65, 234, 86, 10,
],
[
201, 24, 6, 113, 122, 123, 58, 3, 233, 141, 78, 228, 137, 112, 71, 121, 200, 171, 158,
233, 87, 171, 121, 118, 205, 98, 38, 24, 176, 153, 170, 25,
],
[
6, 114, 137, 241, 204, 203, 173, 160, 14, 124, 220, 164, 166, 224, 0, 253, 255, 68, 40,
182, 248, 135, 226, 25, 213, 247, 45, 116, 94, 147, 107, 3,
],
[
73, 103, 138, 222, 168, 203, 85, 216, 242, 63, 127, 158, 153, 60, 168, 180, 234, 71,
27, 10, 38, 161, 207, 26, 81, 150, 195, 37, 91, 228, 57, 46,
],
],
[
[
188, 220, 107, 162, 250, 116, 137, 134, 75, 73, 102, 28, 11, 158, 166, 162, 77, 99,
159, 21, 166, 195, 208, 99, 28, 0, 51, 64, 126, 222, 203, 28,
],
[
115, 93, 10, 209, 3, 81, 82, 191, 158, 74, 26, 242, 145, 24, 85, 106, 28, 36, 54, 17,
216, 109, 58, 102, 221, 11, 10, 157, 226, 90, 53, 3,
],
[
197, 172, 174, 245, 150, 142, 92, 221, 45, 118, 174, 8, 83, 195, 45, 83, 221, 212, 122,
239, 218, 103, 89, 56, 184, 102, 73, 70, 1, 40, 246, 54,
],
[
131, 77, 239, 236, 59, 58, 35, 163, 25, 57, 251, 93, 224, 202, 225, 84, 189, 195, 1,
234, 156, 138, 3, 2, 102, 170, 173, 235, 97, 41, 224, 0,
],
[
251, 165, 141, 221, 2, 154, 174, 224, 120, 187, 163, 188, 37, 146, 49, 193, 150, 241,
183, 33, 12, 228, 96, 92, 105, 198, 238, 59, 247, 172, 247, 54,
],
[
31, 84, 10, 130, 68, 107, 203, 153, 201, 34, 69, 151, 1, 180, 37, 198, 113, 64, 82,
116, 116, 142, 251, 62, 22, 122, 138, 130, 200, 159, 145, 2,
],
[
229, 126, 102, 192, 242, 5, 109, 247, 248, 70, 34, 78, 35, 23, 81, 67, 34, 226, 133,
119, 200, 242, 142, 111, 223, 102, 159, 61, 162, 226, 222, 11,
],
[
171, 0, 253, 102, 188, 223, 208, 250, 186, 183, 127, 172, 10, 41, 201, 173, 242, 156,
106, 219, 236, 139, 76, 115, 200, 123, 176, 228, 181, 248, 121, 38,
],
],
[
[
187, 71, 125, 130, 250, 45, 125, 44, 56, 31, 103, 55, 71, 87, 166, 228, 184, 12, 252,
79, 26, 221, 65, 188, 62, 254, 222, 87, 189, 71, 43, 0,
],
[
248, 127, 55, 175, 11, 237, 134, 201, 211, 212, 93, 115, 63, 118, 15, 121, 71, 55, 176,
74, 3, 75, 20, 100, 177, 194, 39, 92, 67, 109, 243, 38,
],
[
147, 188, 248, 11, 127, 3, 176, 153, 109, 5, 65, 101, 2, 46, 70, 203, 246, 245, 254,
67, 193, 214, 156, 21, 116, 165, 60, 79, 219, 45, 180, 47,
],
[
78, 126, 47, 15, 17, 83, 240, 144, 40, 174, 95, 250, 144, 43, 132, 67, 241, 189, 140,
244, 41, 221, 164, 186, 104, 156, 223, 233, 160, 99, 190, 39,
],
[
29, 119, 16, 42, 190, 69, 200, 191, 3, 160, 164, 28, 189, 135, 85, 63, 59, 121, 213,
143, 9, 96, 150, 14, 21, 93, 132, 57, 4, 165, 174, 12,
],
[
54, 200, 34, 46, 89, 210, 152, 121, 245, 147, 150, 48, 193, 246, 108, 154, 243, 12, 10,
10, 97, 83, 225, 116, 187, 177, 176, 80, 248, 185, 5, 38,
],
[
245, 84, 103, 49, 77, 27, 84, 143, 30, 40, 54, 249, 178, 71, 191, 135, 199, 72, 204,
162, 75, 110, 203, 246, 193, 61, 70, 158, 74, 154, 13, 45,
],
[
123, 98, 28, 217, 129, 160, 71, 205, 19, 41, 168, 124, 76, 145, 108, 71, 57, 60, 26,
154, 163, 64, 250, 13, 52, 179, 197, 193, 54, 184, 29, 32,
],
],
[
[
103, 140, 102, 88, 162, 193, 224, 59, 243, 31, 145, 100, 116, 71, 36, 129, 94, 248, 33,
0, 102, 46, 146, 206, 22, 255, 216, 58, 61, 118, 226, 47,
],
[
21, 127, 228, 231, 155, 190, 28, 145, 48, 160, 35, 104, 47, 120, 243, 107, 145, 118,
199, 126, 138, 164, 246, 143, 153, 59, 153, 209, 81, 118, 167, 9,
],
[
4, 84, 44, 30, 90, 253, 226, 166, 218, 12, 39, 214, 231, 241, 223, 87, 87, 82, 93, 220,
65, 132, 166, 75, 221, 33, 236, 113, 198, 43, 210, 39,
],
[
243, 54, 41, 143, 244, 171, 75, 158, 218, 230, 55, 35, 236, 18, 40, 55, 157, 139, 180,
29, 58, 159, 88, 208, 214, 87, 168, 227, 93, 211, 194, 17,
],
[
97, 131, 219, 190, 19, 178, 244, 173, 141, 143, 113, 3, 27, 63, 35, 185, 170, 43, 75,
64, 75, 38, 5, 13, 123, 39, 147, 243, 141, 122, 217, 39,
],
[
3, 24, 126, 200, 122, 92, 125, 221, 95, 205, 139, 145, 231, 77, 223, 96, 84, 39, 33,
66, 139, 41, 82, 182, 22, 102, 95, 173, 66, 125, 77, 21,
],
[
27, 50, 52, 183, 190, 198, 236, 248, 71, 251, 120, 132, 192, 227, 113, 36, 155, 81,
225, 48, 72, 17, 246, 99, 208, 242, 236, 93, 2, 19, 53, 31,
],
[
99, 18, 31, 165, 229, 52, 216, 52, 162, 62, 66, 1, 190, 22, 69, 133, 11, 126, 106, 165,
131, 180, 218, 253, 238, 124, 3, 16, 42, 196, 148, 57,
],
],
[
[
0, 209, 105, 72, 69, 130, 81, 154, 136, 174, 169, 182, 42, 150, 112, 115, 234, 136, 47,
170, 158, 213, 211, 65, 178, 62, 18, 172, 135, 59, 253, 19,
],
[
145, 192, 219, 168, 214, 190, 54, 248, 68, 248, 196, 148, 4, 254, 61, 193, 67, 218,
131, 110, 235, 60, 159, 101, 200, 218, 208, 195, 30, 249, 163, 32,
],
[
21, 246, 3, 74, 137, 246, 202, 207, 71, 59, 198, 73, 117, 224, 124, 57, 2, 82, 110, 6,
190, 80, 143, 143, 113, 62, 127, 122, 164, 202, 6, 54,
],
[
252, 245, 11, 63, 63, 70, 60, 82, 15, 154, 188, 35, 211, 222, 252, 180, 109, 109, 98,
69, 197, 240, 137, 46, 189, 8, 167, 87, 15, 179, 18, 12,
],
[
125, 206, 204, 128, 43, 62, 39, 36, 246, 164, 44, 6, 250, 83, 14, 207, 53, 201, 166,
231, 175, 110, 140, 200, 48, 239, 20, 171, 46, 80, 115, 54,
],
[
167, 7, 74, 225, 61, 229, 21, 154, 196, 11, 247, 27, 158, 112, 217, 238, 57, 53, 63,
251, 162, 91, 168, 86, 37, 203, 207, 119, 68, 135, 205, 9,
],
[
84, 187, 71, 200, 46, 254, 136, 13, 25, 137, 121, 128, 232, 221, 40, 0, 175, 232, 153,
227, 181, 162, 29, 67, 225, 234, 249, 102, 82, 171, 226, 1,
],
[
24, 185, 170, 6, 35, 57, 108, 85, 245, 134, 216, 239, 33, 12, 223, 38, 227, 73, 145,
100, 25, 14, 244, 177, 84, 38, 101, 67, 21, 96, 249, 61,
],
],
[
[
57, 9, 82, 174, 160, 195, 27, 106, 241, 225, 207, 16, 11, 131, 29, 63, 187, 187, 5, 76,
34, 39, 136, 124, 56, 25, 58, 99, 70, 116, 170, 19,
],
[
143, 6, 32, 114, 74, 44, 29, 53, 226, 34, 62, 232, 111, 63, 201, 203, 46, 115, 209,
118, 31, 27, 1, 120, 254, 70, 252, 80, 5, 111, 123, 55,
],
[
62, 18, 214, 41, 0, 12, 4, 12, 145, 201, 12, 6, 179, 4, 20, 84, 36, 155, 8, 99, 181,
18, 150, 144, 203, 228, 172, 135, 166, 152, 214, 8,
],
[
49, 93, 249, 139, 121, 113, 205, 158, 145, 118, 40, 96, 206, 154, 71, 190, 146, 65,
233, 104, 83, 91, 25, 118, 176, 14, 149, 115, 137, 27, 223, 41,
],
[
116, 160, 29, 244, 254, 193, 228, 122, 194, 168, 126, 1, 222, 247, 90, 191, 253, 101,
123, 197, 178, 127, 30, 113, 38, 73, 48, 240, 82, 52, 161, 12,
],
[
156, 145, 203, 40, 113, 83, 199, 161, 230, 196, 203, 227, 217, 212, 254, 139, 37, 215,
39, 230, 190, 141, 119, 120, 87, 23, 61, 21, 3, 209, 179, 47,
],
[
179, 114, 238, 159, 43, 22, 64, 61, 207, 56, 101, 90, 62, 245, 27, 21, 165, 0, 205, 34,
104, 32, 170, 75, 215, 255, 83, 74, 123, 73, 159, 19,
],
[
14, 208, 162, 223, 209, 5, 175, 15, 1, 78, 222, 82, 21, 113, 25, 129, 103, 64, 139, 21,
226, 245, 199, 114, 252, 69, 133, 254, 128, 63, 61, 13,
],
],
[
[
255, 187, 20, 3, 51, 61, 230, 80, 83, 233, 71, 190, 94, 131, 225, 143, 139, 246, 196,
161, 165, 85, 92, 167, 71, 198, 83, 10, 164, 120, 89, 26,
],
[
250, 108, 167, 151, 249, 92, 38, 36, 21, 96, 210, 31, 41, 91, 113, 183, 104, 192, 3,
45, 165, 253, 37, 75, 239, 245, 28, 148, 5, 255, 134, 60,
],
[
59, 154, 220, 255, 37, 98, 169, 60, 50, 196, 202, 240, 225, 57, 165, 129, 255, 66, 169,
162, 7, 30, 198, 27, 160, 208, 193, 106, 29, 119, 104, 48,
],
[
137, 180, 21, 151, 27, 173, 213, 11, 238, 163, 104, 192, 171, 59, 79, 249, 123, 55,
183, 8, 94, 117, 32, 48, 41, 141, 231, 207, 61, 135, 104, 2,
],
[
242, 254, 15, 0, 58, 49, 204, 28, 27, 56, 2, 67, 248, 104, 160, 32, 214, 242, 10, 206,
233, 61, 23, 103, 180, 53, 179, 198, 56, 254, 65, 6,
],
[
136, 214, 253, 248, 156, 140, 42, 172, 221, 187, 160, 233, 86, 213, 239, 5, 110, 252,
70, 18, 193, 29, 156, 156, 136, 70, 167, 59, 98, 223, 7, 30,
],
[
84, 25, 227, 152, 61, 51, 53, 59, 135, 229, 159, 248, 6, 39, 151, 139, 121, 149, 226,
142, 126, 136, 248, 196, 93, 176, 131, 254, 221, 204, 179, 36,
],
[
198, 74, 99, 58, 59, 34, 82, 94, 95, 64, 17, 241, 173, 114, 211, 57, 124, 181, 140,
102, 105, 79, 13, 1, 60, 121, 143, 88, 192, 253, 159, 47,
],
],
[
[
90, 115, 165, 218, 163, 197, 210, 143, 213, 125, 1, 77, 74, 165, 200, 244, 80, 39, 20,
247, 86, 120, 109, 109, 93, 7, 209, 199, 109, 12, 144, 46,
],
[
231, 44, 48, 128, 109, 202, 114, 192, 218, 67, 233, 141, 64, 251, 104, 41, 58, 212, 60,
65, 93, 58, 34, 149, 128, 90, 30, 197, 191, 244, 8, 37,
],
[
56, 47, 18, 80, 195, 143, 175, 35, 183, 225, 201, 236, 138, 29, 26, 229, 194, 202, 13,
43, 71, 188, 3, 204, 12, 15, 218, 207, 15, 83, 219, 39,
],
[
50, 71, 182, 171, 33, 129, 211, 168, 40, 85, 193, 218, 165, 54, 220, 203, 164, 124, 8,
37, 19, 210, 8, 253, 120, 158, 239, 239, 28, 195, 253, 37,
],
[
245, 191, 155, 103, 118, 221, 209, 204, 89, 48, 249, 160, 180, 1, 114, 3, 254, 220, 94,
244, 221, 122, 224, 55, 184, 106, 99, 11, 236, 89, 211, 38,
],
[
182, 208, 168, 152, 15, 192, 45, 31, 93, 181, 13, 203, 128, 82, 126, 145, 129, 220, 19,
252, 188, 247, 49, 216, 218, 198, 178, 70, 180, 209, 175, 22,
],
[
72, 71, 200, 22, 21, 120, 50, 111, 112, 195, 141, 79, 49, 52, 98, 8, 37, 130, 142, 13,
78, 197, 15, 92, 203, 50, 108, 82, 109, 254, 158, 12,
],
[
71, 44, 114, 76, 152, 26, 79, 25, 44, 244, 191, 178, 150, 102, 34, 230, 54, 251, 209,
155, 90, 28, 81, 49, 127, 246, 116, 238, 106, 105, 196, 29,
],
],
[
[
208, 87, 78, 186, 184, 128, 38, 190, 131, 156, 221, 119, 87, 12, 144, 4, 240, 77, 118,
209, 74, 131, 37, 155, 247, 155, 206, 167, 80, 71, 127, 18,
],
[
190, 54, 229, 228, 15, 167, 185, 240, 161, 238, 216, 88, 210, 31, 242, 20, 81, 147, 48,
54, 38, 226, 251, 64, 69, 196, 67, 166, 242, 34, 118, 39,
],
[
58, 171, 187, 174, 13, 247, 253, 15, 102, 171, 48, 63, 136, 157, 55, 28, 117, 130, 104,
23, 145, 203, 155, 105, 121, 249, 115, 106, 88, 114, 86, 11,
],
[
105, 30, 100, 75, 20, 206, 29, 147, 150, 37, 48, 216, 33, 147, 61, 193, 82, 230, 205,
122, 142, 65, 148, 102, 47, 185, 182, 147, 185, 31, 29, 54,
],
[
158, 245, 169, 236, 26, 185, 17, 174, 156, 69, 81, 196, 60, 109, 99, 91, 19, 208, 93,
58, 9, 109, 228, 186, 109, 127, 171, 156, 229, 215, 195, 59,
],
[
23, 42, 4, 183, 91, 177, 2, 172, 168, 182, 158, 185, 157, 118, 199, 184, 237, 203, 60,
170, 35, 121, 162, 7, 130, 171, 121, 207, 32, 2, 227, 62,
],
[
0, 139, 174, 217, 13, 116, 28, 230, 238, 117, 190, 91, 86, 105, 38, 231, 147, 100, 233,
187, 70, 128, 111, 82, 184, 113, 154, 136, 59, 27, 21, 10,
],
[
4, 208, 53, 136, 59, 196, 102, 52, 69, 1, 231, 8, 254, 19, 67, 134, 251, 73, 157, 156,
30, 94, 170, 147, 185, 72, 11, 143, 226, 255, 0, 60,
],
],
[
[
214, 131, 68, 196, 131, 169, 22, 250, 29, 101, 142, 26, 106, 96, 18, 190, 18, 15, 19,
59, 203, 203, 119, 251, 61, 221, 198, 116, 24, 178, 61, 42,
],
[
101, 161, 133, 103, 0, 112, 204, 255, 98, 240, 20, 161, 242, 253, 216, 204, 83, 96, 93,
228, 77, 76, 63, 70, 116, 156, 69, 253, 121, 189, 2, 36,
],
[
156, 83, 226, 206, 4, 35, 12, 137, 209, 181, 109, 81, 194, 119, 188, 216, 30, 233, 135,
220, 213, 40, 74, 152, 49, 14, 0, 3, 223, 41, 238, 54,
],
[
47, 25, 110, 4, 111, 57, 200, 91, 168, 73, 47, 175, 189, 60, 49, 243, 128, 11, 63, 17,
151, 123, 80, 140, 139, 202, 93, 104, 190, 32, 67, 54,
],
[
90, 100, 132, 107, 167, 162, 164, 62, 239, 68, 20, 223, 157, 1, 90, 95, 248, 82, 65,
61, 241, 63, 238, 10, 2, 160, 230, 104, 101, 197, 60, 52,
],
[
41, 144, 80, 156, 134, 224, 6, 48, 188, 57, 30, 205, 84, 135, 190, 75, 213, 94, 16, 72,
11, 96, 41, 117, 75, 60, 62, 133, 29, 133, 105, 15,
],
[
219, 66, 247, 117, 3, 137, 38, 43, 131, 177, 137, 150, 9, 65, 160, 206, 235, 121, 121,
245, 205, 233, 229, 78, 72, 200, 171, 149, 240, 64, 184, 5,
],
[
22, 179, 118, 116, 100, 222, 159, 96, 236, 247, 38, 23, 224, 103, 6, 5, 42, 95, 161, 4,
128, 2, 240, 122, 117, 247, 127, 207, 76, 205, 137, 31,
],
],
[
[
255, 68, 73, 184, 204, 219, 231, 9, 237, 101, 142, 55, 146, 252, 138, 14, 186, 62, 32,
108, 79, 130, 251, 188, 101, 134, 179, 162, 172, 160, 149, 22,
],
[
93, 226, 69, 177, 229, 17, 78, 185, 6, 206, 195, 246, 145, 189, 141, 7, 197, 148, 166,
43, 203, 235, 170, 119, 102, 76, 108, 98, 216, 237, 121, 34,
],
[
211, 167, 46, 90, 228, 111, 217, 129, 255, 3, 113, 207, 200, 221, 28, 48, 33, 62, 31,
245, 116, 175, 130, 128, 180, 252, 132, 178, 56, 58, 16, 2,
],
[
159, 176, 149, 39, 220, 58, 146, 80, 175, 91, 125, 15, 166, 114, 133, 117, 52, 243,
219, 221, 223, 114, 140, 236, 106, 39, 65, 168, 43, 244, 140, 57,
],
[
144, 68, 49, 189, 208, 94, 145, 108, 143, 62, 16, 188, 15, 110, 23, 239, 71, 48, 32,
238, 96, 19, 43, 91, 231, 90, 77, 162, 159, 162, 71, 15,
],
[
103, 8, 114, 153, 156, 97, 188, 167, 128, 217, 58, 42, 208, 82, 234, 142, 53, 71, 10,
38, 177, 2, 13, 35, 8, 49, 196, 134, 215, 255, 42, 54,
],
[
229, 29, 149, 199, 252, 232, 6, 148, 31, 243, 79, 192, 221, 191, 136, 186, 249, 198,
35, 155, 198, 198, 19, 183, 159, 123, 65, 127, 169, 3, 156, 59,
],
[
2, 244, 213, 144, 80, 83, 125, 211, 252, 98, 209, 105, 104, 213, 143, 183, 164, 199,
103, 53, 110, 48, 230, 35, 34, 129, 221, 255, 225, 224, 42, 42,
],
],
[
[
145, 41, 77, 21, 230, 237, 146, 98, 160, 218, 242, 227, 198, 83, 11, 39, 148, 69, 31,
185, 143, 52, 71, 75, 157, 26, 157, 188, 179, 27, 114, 24,
],
[
160, 247, 33, 120, 242, 78, 125, 237, 149, 68, 194, 190, 248, 145, 93, 23, 171, 167,
181, 242, 226, 41, 104, 67, 0, 116, 81, 246, 87, 82, 103, 51,
],
[
84, 35, 131, 165, 134, 206, 147, 191, 7, 3, 253, 142, 49, 128, 111, 47, 53, 169, 88,
17, 31, 193, 20, 98, 19, 173, 111, 175, 134, 186, 166, 27,
],
[
49, 150, 139, 110, 180, 138, 202, 107, 41, 238, 123, 185, 17, 161, 67, 30, 2, 2, 39,
91, 7, 35, 69, 121, 34, 12, 247, 78, 138, 39, 59, 8,
],
[
64, 14, 249, 58, 50, 65, 122, 135, 174, 11, 102, 220, 221, 64, 29, 66, 24, 169, 57,
114, 140, 176, 7, 149, 78, 15, 211, 255, 101, 244, 151, 46,
],
[
127, 185, 215, 42, 158, 164, 234, 37, 140, 239, 228, 75, 189, 8, 197, 4, 206, 24, 136,
191, 73, 206, 141, 195, 85, 123, 141, 189, 82, 250, 65, 21,
],
[
49, 239, 163, 97, 219, 143, 242, 84, 53, 166, 149, 155, 243, 11, 207, 69, 250, 25, 159,
142, 240, 8, 72, 229, 91, 179, 218, 39, 128, 133, 201, 6,
],
[
96, 144, 236, 91, 71, 246, 217, 36, 27, 102, 209, 14, 75, 249, 185, 211, 2, 97, 216,
204, 141, 6, 234, 251, 183, 215, 152, 151, 125, 210, 121, 14,
],
],
];
pub fn generator() -> pallas::Affine {
pallas::Affine::from_xy(
pallas::Base::from_repr(GENERATOR.0).unwrap(),
pallas::Base::from_repr(GENERATOR.1).unwrap(),
)
.unwrap()
}
#[cfg(test)]
mod tests {
use super::super::{NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION};
use super::*;
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
use group::Curve;
use pasta_curves::{
arithmetic::{CurveAffine, CurveExt},
pallas,
};
#[test]
fn generator() {
let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let point = hasher(b"v");
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap());
assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap());
}
#[test]
fn lagrange_coeffs() {
let base = super::generator();
test_lagrange_coeffs(base, NUM_WINDOWS_SHORT);
}
#[test]
fn z() {
let base = super::generator();
test_zs_and_us(base, &Z_SHORT, &U_SHORT, NUM_WINDOWS_SHORT);
}
}

View File

@ -1,257 +0,0 @@
use std::convert::TryInto;
use crate::constants::{self, compute_lagrange_coeffs, H, NUM_WINDOWS, NUM_WINDOWS_SHORT};
use group::ff::PrimeField;
use pasta_curves::pallas;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OrchardFixedBasesFull {
CommitIvkR,
NoteCommitR,
ValueCommitR,
SpendAuthG,
}
impl OrchardFixedBasesFull {
pub fn generator(&self) -> pallas::Affine {
match self {
OrchardFixedBasesFull::CommitIvkR => super::commit_ivk_r::generator(),
OrchardFixedBasesFull::NoteCommitR => super::note_commit_r::generator(),
OrchardFixedBasesFull::ValueCommitR => super::value_commit_r::generator(),
OrchardFixedBasesFull::SpendAuthG => super::spend_auth_g::generator(),
}
}
pub fn u(&self) -> U {
match self {
OrchardFixedBasesFull::CommitIvkR => super::commit_ivk_r::U.into(),
OrchardFixedBasesFull::NoteCommitR => super::note_commit_r::U.into(),
OrchardFixedBasesFull::ValueCommitR => super::value_commit_r::U.into(),
OrchardFixedBasesFull::SpendAuthG => super::spend_auth_g::U.into(),
}
}
}
/// A fixed base to be used in scalar multiplication with a full-width scalar.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OrchardFixedBase {
pub generator: pallas::Affine,
pub lagrange_coeffs: LagrangeCoeffs,
pub z: Z,
pub u: U,
}
impl From<OrchardFixedBasesFull> for OrchardFixedBase {
fn from(base: OrchardFixedBasesFull) -> Self {
let (generator, z, u) = match base {
OrchardFixedBasesFull::CommitIvkR => (
super::commit_ivk_r::generator(),
super::commit_ivk_r::Z.into(),
super::commit_ivk_r::U.into(),
),
OrchardFixedBasesFull::NoteCommitR => (
super::note_commit_r::generator(),
super::note_commit_r::Z.into(),
super::note_commit_r::U.into(),
),
OrchardFixedBasesFull::ValueCommitR => (
super::value_commit_r::generator(),
super::value_commit_r::Z.into(),
super::value_commit_r::U.into(),
),
OrchardFixedBasesFull::SpendAuthG => (
super::spend_auth_g::generator(),
super::spend_auth_g::Z.into(),
super::spend_auth_g::U.into(),
),
};
Self {
generator,
lagrange_coeffs: compute_lagrange_coeffs(generator, NUM_WINDOWS).into(),
z,
u,
}
}
}
/// A fixed base to be used in scalar multiplication with a base field element.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ValueCommitV {
pub generator: pallas::Affine,
pub lagrange_coeffs_short: LagrangeCoeffsShort,
pub z_short: ZShort,
pub u_short: UShort,
}
impl ValueCommitV {
pub fn get() -> Self {
let generator = super::value_commit_v::generator();
Self {
generator,
lagrange_coeffs_short: compute_lagrange_coeffs(generator, NUM_WINDOWS_SHORT).into(),
z_short: super::value_commit_v::Z_SHORT.into(),
u_short: super::value_commit_v::U_SHORT.into(),
}
}
}
/// A fixed base to be used in scalar multiplication with a short signed exponent.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct NullifierK;
impl From<NullifierK> for OrchardFixedBase {
fn from(_nullifier_k: NullifierK) -> Self {
let (generator, z, u) = (
super::nullifier_k::generator(),
super::nullifier_k::Z.into(),
super::nullifier_k::U.into(),
);
Self {
generator,
lagrange_coeffs: compute_lagrange_coeffs(generator, NUM_WINDOWS).into(),
z,
u,
}
}
}
impl NullifierK {
pub fn generator(&self) -> pallas::Affine {
super::nullifier_k::generator()
}
pub fn u(&self) -> U {
super::nullifier_k::U.into()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 8 coefficients per window
pub struct WindowLagrangeCoeffs(pub Box<[pallas::Base; H]>);
impl From<&[pallas::Base; H]> for WindowLagrangeCoeffs {
fn from(array: &[pallas::Base; H]) -> Self {
Self(Box::new(*array))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 85 windows per base (with the exception of ValueCommitV)
pub struct LagrangeCoeffs(pub Box<[WindowLagrangeCoeffs; constants::NUM_WINDOWS]>);
impl From<Vec<WindowLagrangeCoeffs>> for LagrangeCoeffs {
fn from(windows: Vec<WindowLagrangeCoeffs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<Vec<[pallas::Base; H]>> for LagrangeCoeffs {
fn from(arrays: Vec<[pallas::Base; H]>) -> Self {
let windows: Vec<WindowLagrangeCoeffs> = arrays.iter().map(|array| array.into()).collect();
windows.into()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 22 windows for ValueCommitV
pub struct LagrangeCoeffsShort(pub Box<[WindowLagrangeCoeffs; NUM_WINDOWS_SHORT]>);
impl From<Vec<WindowLagrangeCoeffs>> for LagrangeCoeffsShort {
fn from(windows: Vec<WindowLagrangeCoeffs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<Vec<[pallas::Base; H]>> for LagrangeCoeffsShort {
fn from(arrays: Vec<[pallas::Base; H]>) -> Self {
let windows: Vec<WindowLagrangeCoeffs> = arrays.iter().map(|array| array.into()).collect();
windows.into()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 85 Z's per base (with the exception of ValueCommitV)
pub struct Z(pub Box<[pallas::Base; NUM_WINDOWS]>);
impl From<[u64; NUM_WINDOWS]> for Z {
fn from(zs: [u64; NUM_WINDOWS]) -> Self {
Self(
zs.iter()
.map(|z| pallas::Base::from(*z))
.collect::<Vec<_>>()
.into_boxed_slice()
.try_into()
.unwrap(),
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 22 Z's for ValueCommitV
pub struct ZShort(pub Box<[pallas::Base; NUM_WINDOWS_SHORT]>);
impl From<[u64; NUM_WINDOWS_SHORT]> for ZShort {
fn from(zs: [u64; NUM_WINDOWS_SHORT]) -> Self {
Self(
zs.iter()
.map(|z| pallas::Base::from(*z))
.collect::<Vec<_>>()
.into_boxed_slice()
.try_into()
.unwrap(),
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 8 u's per window
pub struct WindowUs(pub Box<[pallas::Base; H]>);
impl From<&[[u8; 32]; H]> for WindowUs {
fn from(window_us: &[[u8; 32]; H]) -> Self {
Self(
window_us
.iter()
.map(|u| pallas::Base::from_repr(*u).unwrap())
.collect::<Vec<_>>()
.into_boxed_slice()
.try_into()
.unwrap(),
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 85 windows per base (with the exception of ValueCommitV)
pub struct U(pub Box<[WindowUs; NUM_WINDOWS]>);
impl From<Vec<WindowUs>> for U {
fn from(windows: Vec<WindowUs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<[[[u8; 32]; H]; NUM_WINDOWS]> for U {
fn from(window_us: [[[u8; 32]; H]; NUM_WINDOWS]) -> Self {
let windows: Vec<WindowUs> = window_us.iter().map(|us| us.into()).collect();
windows.into()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
// 22 windows for ValueCommitV
pub struct UShort(pub Box<[WindowUs; NUM_WINDOWS_SHORT]>);
impl From<Vec<WindowUs>> for UShort {
fn from(windows: Vec<WindowUs>) -> Self {
Self(windows.into_boxed_slice().try_into().unwrap())
}
}
impl From<[[[u8; 32]; H]; NUM_WINDOWS_SHORT]> for UShort {
fn from(window_us: [[[u8; 32]; H]; NUM_WINDOWS_SHORT]) -> Self {
let windows: Vec<WindowUs> = window_us.iter().map(|us| us.into()).collect();
windows.into()
}
}

View File

@ -1,245 +0,0 @@
//! Sinsemilla generators
use super::{OrchardFixedBases, OrchardFixedBasesFull};
use crate::circuit::gadget::sinsemilla::{CommitDomains, HashDomains};
use crate::spec::i2lebsp;
use pasta_curves::{
arithmetic::{CurveAffine, FieldExt},
pallas,
};
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
pub const K: usize = 10;
/// $\frac{1}{2^K}$
pub const INV_TWO_POW_K: [u8; 32] = [
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 240, 63,
];
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
/// of Pallas.
pub const C: usize = 253;
/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$
pub(crate) const L_ORCHARD_MERKLE: usize = 255;
/// SWU hash-to-curve personalization for the Merkle CRH generator
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
/// Generator used in SinsemillaHashToPoint for note commitment
pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
],
[
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
],
);
/// Generator used in SinsemillaHashToPoint for IVK commitment
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
[
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
],
[
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
],
);
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
[
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
],
[
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
],
);
pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
assert!(bits.len() == K);
bits.iter()
.enumerate()
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
}
/// The sequence of K bits in little-endian order representing an integer
/// up to `2^K` - 1.
pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] {
assert!(int < (1 << K));
i2lebsp(int as u64)
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardHashDomains {
NoteCommit,
CommitIvk,
MerkleCrh,
}
#[allow(non_snake_case)]
impl HashDomains<pallas::Affine> for OrchardHashDomains {
fn Q(&self) -> pallas::Affine {
match self {
OrchardHashDomains::CommitIvk => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap(),
pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap(),
)
.unwrap(),
OrchardHashDomains::NoteCommit => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(),
pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(),
)
.unwrap(),
OrchardHashDomains::MerkleCrh => pallas::Affine::from_xy(
pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap(),
pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap(),
)
.unwrap(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrchardCommitDomains {
NoteCommit,
CommitIvk,
}
impl CommitDomains<pallas::Affine, OrchardFixedBases, OrchardHashDomains> for OrchardCommitDomains {
fn r(&self) -> OrchardFixedBasesFull {
match self {
Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR,
Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR,
}
}
fn hash_domain(&self) -> OrchardHashDomains {
match self {
Self::NoteCommit => OrchardHashDomains::NoteCommit,
Self::CommitIvk => OrchardHashDomains::CommitIvk,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::{
fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION},
sinsemilla::MERKLE_CRH_PERSONALIZATION,
};
use crate::primitives::sinsemilla::{CommitDomain, HashDomain};
use group::{ff::PrimeField, Curve};
use halo2::arithmetic::CurveAffine;
use halo2::pasta::pallas;
use rand::{self, rngs::OsRng, Rng};
#[test]
// Nodes in the Merkle tree are Pallas base field elements.
fn l_orchard_merkle() {
assert_eq!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize);
}
#[test]
fn lebs2ip_k_round_trip() {
let mut rng = OsRng;
{
let int = rng.gen_range(0..(1 << K));
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
}
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
}
#[test]
fn i2lebsp_k_round_trip() {
{
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [false; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
{
let bitstring = [true; K];
assert_eq!(
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
bitstring
);
}
}
#[test]
fn q_note_commitment_m() {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap()
);
}
#[test]
fn q_commit_ivk_m() {
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap()
);
}
#[test]
fn q_merkle_crh() {
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = domain.Q();
let coords = point.to_affine().coordinates().unwrap();
assert_eq!(
*coords.x(),
pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap()
);
assert_eq!(
*coords.y(),
pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap()
);
}
#[test]
fn inv_two_pow_k() {
let two_pow_k = pallas::Base::from(1u64 << K);
let inv_two_pow_k = pallas::Base::from_repr(INV_TWO_POW_K).unwrap();
assert_eq!(two_pow_k * inv_two_pow_k, pallas::Base::one());
}
}

View File

@ -1,18 +0,0 @@
/// Takes in an FnMut closure and returns a constant-length array with elements of
/// type `Output`.
pub fn gen_const_array<Output: Copy + Default, const LEN: usize>(
closure: impl FnMut(usize) -> Output,
) -> [Output; LEN] {
gen_const_array_with_default(Default::default(), closure)
}
pub(crate) fn gen_const_array_with_default<Output: Copy, const LEN: usize>(
default_value: Output,
mut closure: impl FnMut(usize) -> Output,
) -> [Output; LEN] {
let mut ret: [Output; LEN] = [default_value; LEN];
for (bit, val) in ret.iter_mut().zip((0..LEN).map(|idx| closure(idx))) {
*bit = val;
}
ret
}

View File

@ -1,925 +0,0 @@
//! Key structures for Orchard.
use std::convert::{TryFrom, TryInto};
use std::io::{self, Read, Write};
use std::mem;
use aes::Aes256;
use blake2b_simd::{Hash as Blake2bHash, Params};
use fpe::ff1::{BinaryNumeralString, FF1};
use group::{
ff::{Field, PrimeField},
prime::PrimeCurveAffine,
Curve, GroupEncoding,
};
use pasta_curves::pallas;
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_note_encryption::EphemeralKeyBytes;
use crate::{
address::Address,
primitives::redpallas::{self, SpendAuth},
spec::{
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_nf, to_base, to_scalar,
NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar, PrfExpand,
},
zip32::{self, ChildIndex, ExtendedSpendingKey},
};
const KDF_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_OrchardKDF";
const ZIP32_PURPOSE: u32 = 32;
/// A spending key, from which all key material is derived.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug, Copy, Clone)]
pub struct SpendingKey([u8; 32]);
impl ConstantTimeEq for SpendingKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.to_bytes().ct_eq(other.to_bytes())
}
}
impl SpendingKey {
/// Generates a random spending key.
///
/// This is only used when generating dummy notes. Real spending keys should be
/// derived according to [ZIP 32].
///
/// [ZIP 32]: https://zips.z.cash/zip-0032
pub(crate) fn random(rng: &mut impl RngCore) -> Self {
loop {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
let sk = SpendingKey::from_bytes(bytes);
if sk.is_some().into() {
break sk.unwrap();
}
}
}
/// Constructs an Orchard spending key from uniformly-random bytes.
///
/// Returns `None` if the bytes do not correspond to a valid Orchard spending key.
pub fn from_bytes(sk: [u8; 32]) -> CtOption<Self> {
let sk = SpendingKey(sk);
// If ask = 0, discard this key. We call `derive_inner` rather than
// `SpendAuthorizingKey::from` here because we only need to know
// whether ask = 0; the adjustment to potentially negate ask is not
// needed. Also, `from` would panic on ask = 0.
let ask = SpendAuthorizingKey::derive_inner(&sk);
// If ivk = ⊥, discard this key.
let ivk = KeyAgreementPrivateKey::derive_inner(&(&sk).into());
CtOption::new(sk, !(ask.is_zero() | ivk.is_none()))
}
/// Returns the raw bytes of the spending key.
pub fn to_bytes(&self) -> &[u8; 32] {
&self.0
}
/// Derives the Orchard spending key for the given seed, coin type, and account.
pub fn from_zip32_seed(
seed: &[u8],
coin_type: u32,
account: u32,
) -> Result<Self, zip32::Error> {
// Call zip32 logic
let path = &[
ChildIndex::try_from(ZIP32_PURPOSE)?,
ChildIndex::try_from(coin_type)?,
ChildIndex::try_from(account)?,
];
ExtendedSpendingKey::from_path(seed, path).map(|esk| esk.sk())
}
}
/// A spend authorizing key, used to create spend authorization signatures.
/// This type enforces that the corresponding public point (ak^) has ỹ = 0.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
impl SpendAuthorizingKey {
/// Derives ask from sk. Internal use only, does not enforce all constraints.
fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
to_scalar(PrfExpand::OrchardAsk.expand(&sk.0))
}
/// Randomizes this spend authorizing key with the given `randomizer`.
///
/// The resulting key can be used to actually sign a spend.
pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::SigningKey<SpendAuth> {
self.0.randomize(randomizer)
}
}
impl From<&SpendingKey> for SpendAuthorizingKey {
fn from(sk: &SpendingKey) -> Self {
let ask = Self::derive_inner(sk);
// SpendingKey cannot be constructed such that this assertion would fail.
assert!(!bool::from(ask.is_zero()));
// TODO: Add TryFrom<S::Scalar> for SpendAuthorizingKey.
let ret = SpendAuthorizingKey(ask.to_repr().try_into().unwrap());
// If the last bit of repr_P(ak) is 1, negate ask.
if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
SpendAuthorizingKey((-ask).to_repr().try_into().unwrap())
} else {
ret
}
}
}
/// A key used to validate spend authorization signatures.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
/// Note that this is $\mathsf{ak}^\mathbb{P}$, which by construction is equivalent to
/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug, Clone, PartialOrd, Ord)]
pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
fn from(ask: &SpendAuthorizingKey) -> Self {
SpendValidatingKey((&ask.0).into())
}
}
impl From<&SpendValidatingKey> for pallas::Point {
fn from(spend_validating_key: &SpendValidatingKey) -> pallas::Point {
pallas::Point::from_bytes(&(&spend_validating_key.0).into()).unwrap()
}
}
impl PartialEq for SpendValidatingKey {
fn eq(&self, other: &Self) -> bool {
<[u8; 32]>::from(&self.0).eq(&<[u8; 32]>::from(&other.0))
}
}
impl Eq for SpendValidatingKey {}
impl SpendValidatingKey {
/// Randomizes this spend validating key with the given `randomizer`.
pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::VerificationKey<SpendAuth> {
self.0.randomize(randomizer)
}
/// Converts this spend validating key to its serialized form,
/// I2LEOSP_256(ak).
pub(crate) fn to_bytes(&self) -> [u8; 32] {
// This is correct because the wrapped point must have ỹ = 0, and
// so the point repr is the same as I2LEOSP of its x-coordinate.
<[u8; 32]>::from(&self.0)
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
<[u8; 32]>::try_from(bytes)
.ok()
.and_then(|b| {
// Structural validity checks for ak_P:
// - The point must not be the identity
// (which for Pallas is canonically encoded as all-zeroes).
// - The sign of the y-coordinate must be positive.
if b != [0; 32] && b[31] & 0x80 == 0 {
<redpallas::VerificationKey<SpendAuth>>::try_from(b).ok()
} else {
None
}
})
.map(SpendValidatingKey)
}
}
/// A key used to derive [`Nullifier`]s from [`Note`]s.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [`Nullifier`]: crate::note::Nullifier
/// [`Note`]: crate::note::Note
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct NullifierDerivingKey(pallas::Base);
impl NullifierDerivingKey {
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
}
impl From<&SpendingKey> for NullifierDerivingKey {
fn from(sk: &SpendingKey) -> Self {
NullifierDerivingKey(to_base(PrfExpand::OrchardNk.expand(&sk.0)))
}
}
impl NullifierDerivingKey {
pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
prf_nf(self.0, rho)
}
/// Converts this nullifier deriving key to its serialized form.
pub(crate) fn to_bytes(&self) -> [u8; 32] {
<[u8; 32]>::from(self.0)
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey);
if nk.is_some().into() {
Some(nk.unwrap())
} else {
None
}
}
}
/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct CommitIvkRandomness(pallas::Scalar);
impl From<&SpendingKey> for CommitIvkRandomness {
fn from(sk: &SpendingKey) -> Self {
CommitIvkRandomness(to_scalar(PrfExpand::OrchardRivk.expand(&sk.0)))
}
}
impl CommitIvkRandomness {
pub(crate) fn inner(&self) -> pallas::Scalar {
self.0
}
/// Converts this nullifier deriving key to its serialized form.
pub(crate) fn to_bytes(&self) -> [u8; 32] {
<[u8; 32]>::from(self.0)
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness);
if rivk.is_some().into() {
Some(rivk.unwrap())
} else {
None
}
}
}
/// A key that provides the capability to view incoming and outgoing transactions.
///
/// This key is useful anywhere you need to maintain accurate balance, but do not want the
/// ability to spend funds (such as a view-only wallet).
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FullViewingKey {
ak: SpendValidatingKey,
nk: NullifierDerivingKey,
rivk: CommitIvkRandomness,
}
impl From<&SpendingKey> for FullViewingKey {
fn from(sk: &SpendingKey) -> Self {
FullViewingKey {
ak: (&SpendAuthorizingKey::from(sk)).into(),
nk: sk.into(),
rivk: sk.into(),
}
}
}
impl From<&ExtendedSpendingKey> for FullViewingKey {
fn from(extsk: &ExtendedSpendingKey) -> Self {
(&extsk.sk()).into()
}
}
impl From<FullViewingKey> for SpendValidatingKey {
fn from(fvk: FullViewingKey) -> Self {
fvk.ak
}
}
impl FullViewingKey {
pub(crate) fn nk(&self) -> &NullifierDerivingKey {
&self.nk
}
pub(crate) fn rivk(&self) -> &CommitIvkRandomness {
&self.rivk
}
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
let k = self.rivk.0.to_repr();
let b = [(&self.ak.0).into(), self.nk.0.to_repr()];
let r = PrfExpand::OrchardDkOvk.with_ad_slices(&k, &[&b[0][..], &b[1][..]]);
(
DiversifierKey(r[..32].try_into().unwrap()),
OutgoingViewingKey(r[32..].try_into().unwrap()),
)
}
/// Returns the default payment address for this key.
pub fn default_address(&self) -> Address {
IncomingViewingKey::from(self).default_address()
}
/// Returns the payment address for this key at the given index.
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
IncomingViewingKey::from(self).address_at(j)
}
/// Returns the payment address for this key corresponding to the given diversifier.
pub fn address(&self, d: Diversifier) -> Address {
// Shortcut: we don't need to derive DiversifierKey.
KeyAgreementPrivateKey::from(self).address(d)
}
/// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
///
/// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
let ak_raw: [u8; 32] = self.ak.0.clone().into();
writer.write_all(&ak_raw)?;
writer.write_all(&self.nk.0.to_repr())?;
writer.write_all(&self.rivk.0.to_repr())?;
Ok(())
}
/// Parses a full viewing key from its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
///
/// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut data = [0u8; 96];
reader.read_exact(&mut data)?;
Self::from_bytes(&data).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Unable to deserialize a valid Orchard FullViewingKey from bytes".to_owned(),
)
})
}
/// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
///
/// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
pub fn to_bytes(&self) -> [u8; 96] {
let mut result = [0u8; 96];
self.write(&mut result[..])
.expect("should be able to serialize a FullViewingKey");
result
}
/// Parses a full viewing key from its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
///
/// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
pub fn from_bytes(bytes: &[u8; 96]) -> Option<Self> {
let ak = SpendValidatingKey::from_bytes(&bytes[..32])?;
let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
Some(FullViewingKey { ak, nk, rivk })
}
}
/// A key that provides the capability to derive a sequence of diversifiers.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DiversifierKey([u8; 32]);
impl From<&FullViewingKey> for DiversifierKey {
fn from(fvk: &FullViewingKey) -> Self {
fvk.derive_dk_ovk().0
}
}
/// The index for a particular diversifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DiversifierIndex([u8; 11]);
macro_rules! di_from {
($n:ident) => {
impl From<$n> for DiversifierIndex {
fn from(j: $n) -> Self {
let mut j_bytes = [0; 11];
j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes());
DiversifierIndex(j_bytes)
}
}
};
}
di_from!(u32);
di_from!(u64);
di_from!(usize);
impl From<[u8; 11]> for DiversifierIndex {
fn from(j_bytes: [u8; 11]) -> Self {
DiversifierIndex(j_bytes)
}
}
impl DiversifierKey {
/// Returns the diversifier at index 0.
pub fn default_diversifier(&self) -> Diversifier {
self.get(0u32)
}
/// Returns the diversifier at the given index.
pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
let enc = ff
.encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.into().0[..]))
.unwrap();
Diversifier(enc.to_bytes_le().try_into().unwrap())
}
/// Return the raw bytes of the diversifier key
pub fn to_bytes(&self) -> &[u8; 32] {
&self.0
}
/// Construct a diversifier key from bytes
pub fn from_bytes(bytes: [u8; 32]) -> Self {
DiversifierKey(bytes)
}
}
/// A diversifier that can be used to derive a specific [`Address`] from a
/// [`FullViewingKey`] or [`IncomingViewingKey`].
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Diversifier([u8; 11]);
impl Diversifier {
pub(crate) fn from_bytes(d: [u8; 11]) -> Self {
Diversifier(d)
}
/// Returns the byte array corresponding to this diversifier.
pub fn as_array(&self) -> &[u8; 11] {
&self.0
}
}
/// The private key $\mathsf{ivk}$ used in $KA^{Orchard}$, for decrypting incoming notes.
///
/// In Sapling this is what was encoded as an incoming viewing key. For Orchard, we store
/// both this and [`DiversifierKey`] inside [`IncomingViewingKey`] for usability (to
/// enable deriving the default address for an incoming viewing key), while this separate
/// type represents $\mathsf{ivk}$.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
///
/// # Implementation notes
///
/// We store $\mathsf{ivk}$ in memory as a scalar instead of a base, so that we aren't
/// incurring an expensive serialize-and-parse step every time we use it (e.g. for trial
/// decryption of notes). When we actually want to serialize ivk, we're guaranteed to get
/// a valid base field element encoding, because we always construct ivk from an integer
/// in the correct range.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct KeyAgreementPrivateKey(NonZeroPallasScalar);
impl From<&FullViewingKey> for KeyAgreementPrivateKey {
fn from(fvk: &FullViewingKey) -> Self {
// KeyAgreementPrivateKey cannot be constructed such that this unwrap would fail.
let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
KeyAgreementPrivateKey(ivk.into())
}
}
impl KeyAgreementPrivateKey {
/// Derives ivk from fvk. Internal use only, does not enforce all constraints.
fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
}
/// Returns the payment address for this key corresponding to the given diversifier.
fn address(&self, d: Diversifier) -> Address {
let pk_d = DiversifiedTransmissionKey::derive_inner(self, &d);
Address::from_parts(d, pk_d)
}
}
/// A key that provides the capability to detect and decrypt incoming notes from the block
/// chain, without being able to spend the notes or detect when they are spent.
///
/// This key is useful in situations where you only need the capability to detect inbound
/// payments, such as merchant terminals.
///
/// This key is not suitable for use on its own in a wallet, as it cannot maintain
/// accurate balance. You should use a [`FullViewingKey`] instead.
///
/// Defined in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardinviewingkeyencoding].
///
/// [orchardinviewingkeyencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardinviewingkeyencoding
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct IncomingViewingKey {
dk: DiversifierKey,
ivk: KeyAgreementPrivateKey,
}
impl From<&FullViewingKey> for IncomingViewingKey {
fn from(fvk: &FullViewingKey) -> Self {
IncomingViewingKey {
dk: fvk.into(),
ivk: fvk.into(),
}
}
}
impl IncomingViewingKey {
/// Serializes an Orchard incoming viewing key to its raw encoding as specified in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardrawinviewingkeys]
///
/// [orchardrawinviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardinviewingkeyencoding
pub fn to_bytes(&self) -> [u8; 64] {
let mut result = [0u8; 64];
result[..32].copy_from_slice(self.dk.to_bytes());
result[32..].copy_from_slice(&self.ivk.0.to_repr());
result
}
/// Parses an Orchard incoming viewing key from its raw encoding.
pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
NonZeroPallasBase::from_bytes(bytes[32..].try_into().unwrap()).map(|ivk| {
IncomingViewingKey {
dk: DiversifierKey(bytes[..32].try_into().unwrap()),
ivk: KeyAgreementPrivateKey(ivk.into()),
}
})
}
/// Returns the default payment address for this key.
pub fn default_address(&self) -> Address {
self.address(self.dk.default_diversifier())
}
/// Returns the payment address for this key at the given index.
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
self.address(self.dk.get(j))
}
/// Returns the payment address for this key corresponding to the given diversifier.
pub fn address(&self, d: Diversifier) -> Address {
self.ivk.address(d)
}
}
/// A key that provides the capability to recover outgoing transaction information from
/// the block chain.
///
/// This key is not suitable for use on its own in a wallet, as it cannot maintain
/// accurate balance. You should use a [`FullViewingKey`] instead.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug, Clone)]
pub struct OutgoingViewingKey([u8; 32]);
impl From<&FullViewingKey> for OutgoingViewingKey {
fn from(fvk: &FullViewingKey) -> Self {
fvk.derive_dk_ovk().1
}
}
impl From<[u8; 32]> for OutgoingViewingKey {
fn from(ovk: [u8; 32]) -> Self {
OutgoingViewingKey(ovk)
}
}
impl AsRef<[u8; 32]> for OutgoingViewingKey {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
/// The diversified transmission key for a given payment address.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
impl DiversifiedTransmissionKey {
pub(crate) fn inner(&self) -> NonIdentityPallasPoint {
self.0
}
}
impl DiversifiedTransmissionKey {
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
pub(crate) fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
Self::derive_inner(&ivk.ivk, d)
}
fn derive_inner(ivk: &KeyAgreementPrivateKey, d: &Diversifier) -> Self {
let g_d = diversify_hash(d.as_array());
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
}
/// $abst_P(bytes)$
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
}
/// $repr_P(self)$
pub(crate) fn to_bytes(self) -> [u8; 32] {
self.0.to_bytes()
}
}
impl Default for DiversifiedTransmissionKey {
fn default() -> Self {
DiversifiedTransmissionKey(NonIdentityPallasPoint::default())
}
}
impl ConditionallySelectable for DiversifiedTransmissionKey {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
DiversifiedTransmissionKey(NonIdentityPallasPoint::conditional_select(
&a.0, &b.0, choice,
))
}
}
/// An ephemeral secret key used to encrypt an output note on-chain.
///
/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
/// practice, `esk` is derived deterministically from the note that it is encrypting.
///
/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{Private} := \mathbb{F}^{\ast}_{r_P}$
///
/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
///
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
#[derive(Debug)]
pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar);
impl ConstantTimeEq for EphemeralSecretKey {
fn ct_eq(&self, other: &Self) -> subtle::Choice {
self.0.ct_eq(&other.0)
}
}
impl EphemeralSecretKey {
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey)
}
pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey {
EphemeralPublicKey(ka_orchard(&self.0, &g_d))
}
pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
SharedSecret(ka_orchard(&self.0, &pk_d.0))
}
}
/// An ephemeral public key used to encrypt an output note on-chain.
///
/// `epk` is "ephemeral" in the sense that each public key is only used once. In practice,
/// `epk` is derived deterministically from the note that it is encrypting.
///
/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{Public} := \mathbb{P}^{\ast}$
///
/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
///
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
#[derive(Debug)]
pub struct EphemeralPublicKey(NonIdentityPallasPoint);
impl EphemeralPublicKey {
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey)
}
pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
EphemeralKeyBytes(self.0.to_bytes())
}
pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret {
SharedSecret(ka_orchard(&ivk.ivk.0, &self.0))
}
}
/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{SharedSecret} := \mathbb{P}^{\ast}$
///
/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
///
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
#[derive(Debug)]
pub struct SharedSecret(NonIdentityPallasPoint);
impl SharedSecret {
/// For checking test vectors only.
#[cfg(test)]
pub(crate) fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
/// Only for use in batched note encryption.
pub(crate) fn batch_to_affine(
shared_secrets: Vec<Option<Self>>,
) -> impl Iterator<Item = Option<pallas::Affine>> {
// Filter out the positions for which ephemeral_key was not a valid encoding.
let secrets: Vec<_> = shared_secrets
.iter()
.filter_map(|s| s.as_ref().map(|s| *(s.0)))
.collect();
// Batch-normalize the shared secrets.
let mut secrets_affine = vec![pallas::Affine::identity(); secrets.len()];
group::Curve::batch_normalize(&secrets, &mut secrets_affine);
// Re-insert the invalid ephemeral_key positions.
let mut secrets_affine = secrets_affine.into_iter();
shared_secrets
.into_iter()
.map(move |s| s.and_then(|_| secrets_affine.next()))
}
/// Defined in [Zcash Protocol Spec § 5.4.5.6: Orchard Key Agreement][concreteorchardkdf].
///
/// [concreteorchardkdf]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkdf
pub(crate) fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key)
}
/// Only for direct use in batched note encryption.
pub(crate) fn kdf_orchard_inner(
secret: pallas::Affine,
ephemeral_key: &EphemeralKeyBytes,
) -> Blake2bHash {
Params::new()
.hash_length(32)
.personal(KDF_ORCHARD_PERSONALIZATION)
.to_state()
.update(&secret.to_bytes())
.update(&ephemeral_key.0)
.finalize()
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use proptest::prelude::*;
use super::{EphemeralSecretKey, SpendingKey};
prop_compose! {
/// Generate a uniformly distributed fake note commitment value.
pub fn arb_spending_key()(
key in prop::array::uniform32(prop::num::u8::ANY)
.prop_map(SpendingKey::from_bytes)
.prop_filter(
"Values must correspond to valid Orchard spending keys.",
|opt| bool::from(opt.is_some())
)
) -> SpendingKey {
key.unwrap()
}
}
prop_compose! {
/// Generate a uniformly distributed fake note commitment value.
pub fn arb_esk()(
esk in prop::array::uniform32(prop::num::u8::ANY)
.prop_map(|b| EphemeralSecretKey::from_bytes(&b))
.prop_filter(
"Values must correspond to valid Orchard ephemeral secret keys.",
|opt| bool::from(opt.is_some())
)
) -> EphemeralSecretKey {
esk.unwrap()
}
}
}
#[cfg(test)]
mod tests {
use ff::PrimeField;
use proptest::prelude::*;
use super::{
testing::{arb_esk, arb_spending_key},
*,
};
use crate::{
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
value::NoteValue,
Note,
};
#[test]
fn spend_validating_key_from_bytes() {
// ak_P must not be the identity.
assert!(SpendValidatingKey::from_bytes(&[0; 32]).is_none());
}
#[test]
fn parsers_reject_invalid() {
assert!(bool::from(
EphemeralSecretKey::from_bytes(&[0xff; 32]).is_none()
));
assert!(bool::from(
EphemeralPublicKey::from_bytes(&[0xff; 32]).is_none()
));
}
proptest! {
#[test]
fn key_agreement(
sk in arb_spending_key(),
esk in arb_esk(),
) {
let ivk = IncomingViewingKey::from(&(&sk).into());
let addr = ivk.default_address();
let epk = esk.derive_public(addr.g_d());
assert!(bool::from(
esk.agree(addr.pk_d()).0.ct_eq(&epk.agree(&ivk).0)
));
}
}
#[test]
fn test_vectors() {
for tv in crate::test_vectors::keys::test_vectors() {
let sk = SpendingKey::from_bytes(tv.sk).unwrap();
let ask: SpendAuthorizingKey = (&sk).into();
assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask);
let ak: SpendValidatingKey = (&ask).into();
assert_eq!(<[u8; 32]>::from(ak.0), tv.ak);
let nk: NullifierDerivingKey = (&sk).into();
assert_eq!(nk.0.to_repr(), tv.nk);
let rivk: CommitIvkRandomness = (&sk).into();
assert_eq!(rivk.0.to_repr(), tv.rivk);
let fvk: FullViewingKey = (&sk).into();
assert_eq!(<[u8; 32]>::from(&fvk.ak.0), tv.ak);
assert_eq!(fvk.nk().0.to_repr(), tv.nk);
assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
let ivk: KeyAgreementPrivateKey = (&fvk).into();
assert_eq!(ivk.0.to_repr(), tv.ivk);
let diversifier = Diversifier(tv.default_d);
let addr = fvk.address(diversifier);
assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
let rho = Nullifier::from_bytes(&tv.note_rho).unwrap();
let note = Note::from_parts(
addr,
NoteValue::from_raw(tv.note_v),
rho,
RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
);
let cmx: ExtractedNoteCommitment = note.commitment().into();
assert_eq!(cmx.to_bytes(), tv.note_cmx);
assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
}
}
}

View File

@ -16,25 +16,5 @@
#![deny(missing_docs)]
#![deny(unsafe_code)]
mod address;
pub mod builder;
pub mod bundle;
pub mod circuit;
mod constants;
pub mod keys;
pub mod note;
pub mod note_encryption;
pub mod primitives;
mod spec;
pub mod tree;
pub mod value;
pub mod zip32;
#[cfg(test)]
mod test_vectors;
pub use address::Address;
pub use bundle::Bundle;
pub use circuit::Proof;
pub use note::Note;
pub use tree::Anchor;

View File

@ -1,284 +0,0 @@
//! Data structures used for note construction.
use group::GroupEncoding;
use pasta_curves::pallas;
use rand::RngCore;
use subtle::CtOption;
use crate::{
keys::{EphemeralSecretKey, FullViewingKey, SpendingKey},
spec::{to_base, to_scalar, NonZeroPallasScalar, PrfExpand},
value::NoteValue,
Address,
};
pub(crate) mod commitment;
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
pub(crate) mod nullifier;
pub use self::nullifier::Nullifier;
/// The ZIP 212 seed randomness for a note.
#[derive(Copy, Clone, Debug)]
pub(crate) struct RandomSeed([u8; 32]);
impl RandomSeed {
pub(crate) fn random(rng: &mut impl RngCore, rho: &Nullifier) -> Self {
loop {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
let rseed = RandomSeed::from_bytes(bytes, rho);
if rseed.is_some().into() {
break rseed.unwrap();
}
}
}
pub(crate) fn from_bytes(rseed: [u8; 32], rho: &Nullifier) -> CtOption<Self> {
let rseed = RandomSeed(rseed);
let esk = rseed.esk_inner(rho);
CtOption::new(rseed, esk.is_some())
}
pub(crate) fn to_bytes(&self) -> &[u8; 32] {
&self.0
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
pub(crate) fn psi(&self, rho: &Nullifier) -> pallas::Base {
to_base(PrfExpand::Psi.with_ad(&self.0, &rho.to_bytes()[..]))
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn esk_inner(&self, rho: &Nullifier) -> CtOption<NonZeroPallasScalar> {
NonZeroPallasScalar::from_scalar(to_scalar(
PrfExpand::Esk.with_ad(&self.0, &rho.to_bytes()[..]),
))
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn esk(&self, rho: &Nullifier) -> NonZeroPallasScalar {
// We can't construct a RandomSeed for which this unwrap fails.
self.esk_inner(rho).unwrap()
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
pub(crate) fn rcm(&self, rho: &Nullifier) -> commitment::NoteCommitTrapdoor {
commitment::NoteCommitTrapdoor(to_scalar(
PrfExpand::Rcm.with_ad(&self.0, &rho.to_bytes()[..]),
))
}
}
/// A discrete amount of funds received by an address.
#[derive(Debug, Copy, Clone)]
pub struct Note {
/// The recipient of the funds.
recipient: Address,
/// The value of this note.
value: NoteValue,
/// A unique creation ID for this note.
///
/// This is set to the nullifier of the note that was spent in the [`Action`] that
/// created this note.
///
/// [`Action`]: crate::bundle::Action
rho: Nullifier,
/// The seed randomness for various note components.
rseed: RandomSeed,
}
impl PartialEq for Note {
fn eq(&self, other: &Self) -> bool {
// Notes are canonically defined by their commitments.
ExtractedNoteCommitment::from(self.commitment())
.eq(&ExtractedNoteCommitment::from(other.commitment()))
}
}
impl Eq for Note {}
impl Note {
pub(crate) fn from_parts(
recipient: Address,
value: NoteValue,
rho: Nullifier,
rseed: RandomSeed,
) -> Self {
Note {
recipient,
value,
rho,
rseed,
}
}
/// Generates a new note.
///
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
pub(crate) fn new(
recipient: Address,
value: NoteValue,
rho: Nullifier,
mut rng: impl RngCore,
) -> Self {
loop {
let note = Note {
recipient,
value,
rho,
rseed: RandomSeed::random(&mut rng, &rho),
};
if note.commitment_inner().is_some().into() {
break note;
}
}
}
/// Generates a dummy spent note.
///
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
pub(crate) fn dummy(
rng: &mut impl RngCore,
rho: Option<Nullifier>,
) -> (SpendingKey, FullViewingKey, Self) {
let sk = SpendingKey::random(rng);
let fvk: FullViewingKey = (&sk).into();
let recipient = fvk.default_address();
let note = Note::new(
recipient,
NoteValue::zero(),
rho.unwrap_or_else(|| Nullifier::dummy(rng)),
rng,
);
(sk, fvk, note)
}
/// Returns the recipient of this note.
pub fn recipient(&self) -> Address {
self.recipient
}
/// Returns the value of this note.
pub fn value(&self) -> NoteValue {
self.value
}
/// Returns the rseed value of this note.
pub(crate) fn rseed(&self) -> &RandomSeed {
&self.rseed
}
/// Derives the ephemeral secret key for this note.
pub(crate) fn esk(&self) -> EphemeralSecretKey {
EphemeralSecretKey(self.rseed.esk(&self.rho))
}
/// Returns rho of this note.
pub fn rho(&self) -> Nullifier {
self.rho
}
/// Derives the commitment to this note.
///
/// Defined in [Zcash Protocol Spec § 3.2: Notes][notes].
///
/// [notes]: https://zips.z.cash/protocol/nu5.pdf#notes
pub fn commitment(&self) -> NoteCommitment {
// `Note` will always have a note commitment by construction.
self.commitment_inner().unwrap()
}
/// Derives the commitment to this note.
///
/// This is the internal fallible API, used to check at construction time that the
/// note has a commitment. Once you have a [`Note`] object, use `note.commitment()`
/// instead.
///
/// Defined in [Zcash Protocol Spec § 3.2: Notes][notes].
///
/// [notes]: https://zips.z.cash/protocol/nu5.pdf#notes
fn commitment_inner(&self) -> CtOption<NoteCommitment> {
let g_d = self.recipient.g_d();
NoteCommitment::derive(
g_d.to_bytes(),
self.recipient.pk_d().to_bytes(),
self.value,
self.rho.0,
self.rseed.psi(&self.rho),
self.rseed.rcm(&self.rho),
)
}
/// Derives the nullifier for this note.
pub fn nullifier(&self, fvk: &FullViewingKey) -> Nullifier {
Nullifier::derive(
fvk.nk(),
self.rho.0,
self.rseed.psi(&self.rho),
self.commitment(),
)
}
}
/// An encrypted note.
#[derive(Debug, Clone)]
pub struct TransmittedNoteCiphertext {
/// The serialization of the ephemeral public key
pub epk_bytes: [u8; 32],
/// The encrypted note ciphertext
pub enc_ciphertext: [u8; 580],
/// An encrypted value that allows the holder of the outgoing cipher
/// key for the note to recover the note plaintext.
pub out_ciphertext: [u8; 80],
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use proptest::prelude::*;
use crate::{
address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue,
};
use super::{Note, RandomSeed};
prop_compose! {
/// Generate an arbitrary random seed
pub(crate) fn arb_rseed()(elems in prop::array::uniform32(prop::num::u8::ANY)) -> RandomSeed {
RandomSeed(elems)
}
}
prop_compose! {
/// Generate an action without authorization data.
pub fn arb_note(value: NoteValue)(
recipient in arb_address(),
rho in arb_nullifier(),
rseed in arb_rseed(),
) -> Note {
Note {
recipient,
value,
rho,
rseed,
}
}
}
}

View File

@ -1,114 +0,0 @@
use std::iter;
use bitvec::{array::BitArray, order::Lsb0};
use group::ff::{PrimeField, PrimeFieldBits};
use pasta_curves::pallas;
use subtle::{ConstantTimeEq, CtOption};
use crate::{
constants::{fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, L_ORCHARD_BASE},
primitives::sinsemilla,
spec::extract_p,
value::NoteValue,
};
#[derive(Debug)]
pub(crate) struct NoteCommitTrapdoor(pub(super) pallas::Scalar);
impl NoteCommitTrapdoor {
pub(crate) fn inner(&self) -> pallas::Scalar {
self.0
}
}
/// A commitment to a note.
#[derive(Clone, Debug)]
pub struct NoteCommitment(pub(super) pallas::Point);
impl NoteCommitment {
pub(crate) fn inner(&self) -> pallas::Point {
self.0
}
}
impl NoteCommitment {
/// $NoteCommit^Orchard$.
///
/// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit].
///
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
pub(super) fn derive(
g_d: [u8; 32],
pk_d: [u8; 32],
v: NoteValue,
rho: pallas::Base,
psi: pallas::Base,
rcm: NoteCommitTrapdoor,
) -> CtOption<Self> {
let domain = sinsemilla::CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
domain
.commit(
iter::empty()
.chain(BitArray::<Lsb0, _>::new(g_d).iter().by_val())
.chain(BitArray::<Lsb0, _>::new(pk_d).iter().by_val())
.chain(v.to_le_bits().iter().by_val())
.chain(rho.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
.chain(psi.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
&rcm.0,
)
.map(NoteCommitment)
}
}
/// The x-coordinate of the commitment to a note.
#[derive(Copy, Clone, Debug)]
pub struct ExtractedNoteCommitment(pub(super) pallas::Base);
impl ExtractedNoteCommitment {
/// Deserialize the extracted note commitment from a byte array.
///
/// This method enforces the [consensus rule][cmxcanon] that the
/// byte representation of cmx MUST be canonical.
///
/// [cmxcanon]: https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_repr(*bytes).map(ExtractedNoteCommitment)
}
/// Serialize the value commitment to its canonical byte representation.
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_repr()
}
}
impl From<NoteCommitment> for ExtractedNoteCommitment {
fn from(cm: NoteCommitment) -> Self {
ExtractedNoteCommitment(extract_p(&cm.0))
}
}
impl ExtractedNoteCommitment {
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
}
impl From<&ExtractedNoteCommitment> for [u8; 32] {
fn from(cmx: &ExtractedNoteCommitment) -> Self {
cmx.to_bytes()
}
}
impl ConstantTimeEq for ExtractedNoteCommitment {
fn ct_eq(&self, other: &Self) -> subtle::Choice {
self.0.ct_eq(&other.0)
}
}
impl PartialEq for ExtractedNoteCommitment {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for ExtractedNoteCommitment {}

View File

@ -1,83 +0,0 @@
use group::{ff::PrimeField, Group};
use halo2::arithmetic::CurveExt;
use pasta_curves::pallas;
use rand::RngCore;
use subtle::CtOption;
use super::NoteCommitment;
use crate::{
keys::NullifierDerivingKey,
spec::{extract_p, mod_r_p},
};
/// A unique nullifier for a note.
#[derive(Clone, Copy, Debug)]
pub struct Nullifier(pub(crate) pallas::Base);
impl Nullifier {
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
///
/// Nullifiers are required by consensus to be unique. For dummy output notes, we get
/// this restriction as intended: the note's $\rho$ value is set to the nullifier of
/// the accompanying spent note within the action, which is constrained by consensus
/// to be unique. In the case of dummy spent notes, we get this restriction by
/// following the chain backwards: the nullifier of the dummy spent note will be
/// constrained by consensus to be unique, and the nullifier's uniqueness is derived
/// from the uniqueness of $\rho$.
///
/// Instead of explicitly sampling for a unique nullifier, we rely here on the size of
/// the base field to make the chance of sampling a colliding nullifier negligible.
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
Nullifier(extract_p(&pallas::Point::random(rng)))
}
/// Deserialize the nullifier from a byte array.
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_repr(*bytes).map(Nullifier)
}
/// Serialize the nullifier to its canonical byte representation.
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_repr()
}
/// $DeriveNullifier$.
///
/// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers].
///
/// [commitmentsandnullifiers]: https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
pub(super) fn derive(
nk: &NullifierDerivingKey,
rho: pallas::Base,
psi: pallas::Base,
cm: NoteCommitment,
) -> Self {
let k = pallas::Point::hash_to_curve("z.cash:Orchard")(b"K");
Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0)))
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use group::Group;
use pasta_curves::{arithmetic::FieldExt, pallas};
use proptest::collection::vec;
use proptest::prelude::*;
use std::convert::TryFrom;
use super::Nullifier;
use crate::spec::extract_p;
prop_compose! {
/// Generate a uniformly distributed nullifier value.
pub fn arb_nullifier()(
bytes in vec(any::<u8>(), 64)
) -> Nullifier {
let point = pallas::Point::generator() * pallas::Scalar::from_bytes_wide(&<[u8; 64]>::try_from(bytes).unwrap());
Nullifier(extract_p(&point))
}
}
}

View File

@ -1,434 +0,0 @@
//! In-band secret distribution for Orchard bundles.
use std::{convert::TryInto, fmt};
use blake2b_simd::{Hash, Params};
use group::ff::PrimeField;
use zcash_note_encryption::{
BatchDomain, Domain, EphemeralKeyBytes, NotePlaintextBytes, OutPlaintextBytes,
OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE,
OUT_PLAINTEXT_SIZE,
};
use crate::{
bundle::Action,
keys::{
DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey,
IncomingViewingKey, OutgoingViewingKey, SharedSecret,
},
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
spec::diversify_hash,
value::{NoteValue, ValueCommitment},
Address, Note,
};
const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock";
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
///
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
pub(crate) fn prf_ock_orchard(
ovk: &OutgoingViewingKey,
cv: &ValueCommitment,
cmx_bytes: &[u8; 32],
ephemeral_key: &EphemeralKeyBytes,
) -> OutgoingCipherKey {
OutgoingCipherKey(
Params::new()
.hash_length(32)
.personal(PRF_OCK_ORCHARD_PERSONALIZATION)
.to_state()
.update(ovk.as_ref())
.update(&cv.to_bytes())
.update(cmx_bytes)
.update(ephemeral_key.as_ref())
.finalize()
.as_bytes()
.try_into()
.unwrap(),
)
}
fn orchard_parse_note_plaintext_without_memo<F>(
domain: &OrchardDomain,
plaintext: &[u8],
get_validated_pk_d: F,
) -> Option<(Note, Address)>
where
F: FnOnce(&Diversifier) -> Option<DiversifiedTransmissionKey>,
{
assert!(plaintext.len() >= COMPACT_NOTE_SIZE);
// Check note plaintext version
if plaintext[0] != 0x02 {
return None;
}
// The unwraps below are guaranteed to succeed by the assertion above
let diversifier = Diversifier::from_bytes(plaintext[1..12].try_into().unwrap());
let value = NoteValue::from_bytes(plaintext[12..20].try_into().unwrap());
let rseed = Option::from(RandomSeed::from_bytes(
plaintext[20..COMPACT_NOTE_SIZE].try_into().unwrap(),
&domain.rho,
))?;
let pk_d = get_validated_pk_d(&diversifier)?;
let recipient = Address::from_parts(diversifier, pk_d);
let note = Note::from_parts(recipient, value, domain.rho, rseed);
Some((note, recipient))
}
/// Orchard-specific note encryption logic.
#[derive(Debug)]
pub struct OrchardDomain {
rho: Nullifier,
}
impl OrchardDomain {
/// Constructs a domain that can be used to trial-decrypt this action's output note.
pub fn for_action<T>(act: &Action<T>) -> Self {
OrchardDomain {
rho: *act.nullifier(),
}
}
}
impl Domain for OrchardDomain {
type EphemeralSecretKey = EphemeralSecretKey;
type EphemeralPublicKey = EphemeralPublicKey;
type SharedSecret = SharedSecret;
type SymmetricKey = Hash;
type Note = Note;
type Recipient = Address;
type DiversifiedTransmissionKey = DiversifiedTransmissionKey;
type IncomingViewingKey = IncomingViewingKey;
type OutgoingViewingKey = OutgoingViewingKey;
type ValueCommitment = ValueCommitment;
type ExtractedCommitment = ExtractedNoteCommitment;
type ExtractedCommitmentBytes = [u8; 32];
type Memo = [u8; 512]; // TODO use a more interesting type
fn derive_esk(note: &Self::Note) -> Option<Self::EphemeralSecretKey> {
Some(note.esk())
}
fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey {
*note.recipient().pk_d()
}
fn ka_derive_public(
note: &Self::Note,
esk: &Self::EphemeralSecretKey,
) -> Self::EphemeralPublicKey {
esk.derive_public(note.recipient().g_d())
}
fn ka_agree_enc(
esk: &Self::EphemeralSecretKey,
pk_d: &Self::DiversifiedTransmissionKey,
) -> Self::SharedSecret {
esk.agree(pk_d)
}
fn ka_agree_dec(
ivk: &Self::IncomingViewingKey,
epk: &Self::EphemeralPublicKey,
) -> Self::SharedSecret {
epk.agree(ivk)
}
fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey {
secret.kdf_orchard(ephemeral_key)
}
fn note_plaintext_bytes(
note: &Self::Note,
_: &Self::Recipient,
memo: &Self::Memo,
) -> NotePlaintextBytes {
let mut np = [0; NOTE_PLAINTEXT_SIZE];
np[0] = 0x02;
np[1..12].copy_from_slice(note.recipient().diversifer().as_array());
np[12..20].copy_from_slice(&note.value().to_bytes());
np[20..52].copy_from_slice(note.rseed().to_bytes());
np[52..].copy_from_slice(memo);
NotePlaintextBytes(np)
}
fn derive_ock(
ovk: &Self::OutgoingViewingKey,
cv: &Self::ValueCommitment,
cmstar_bytes: &Self::ExtractedCommitmentBytes,
ephemeral_key: &EphemeralKeyBytes,
) -> OutgoingCipherKey {
prf_ock_orchard(ovk, cv, cmstar_bytes, ephemeral_key)
}
fn outgoing_plaintext_bytes(
note: &Self::Note,
esk: &Self::EphemeralSecretKey,
) -> OutPlaintextBytes {
let mut op = [0; OUT_PLAINTEXT_SIZE];
op[..32].copy_from_slice(&note.recipient().pk_d().to_bytes());
op[32..].copy_from_slice(&esk.0.to_repr());
OutPlaintextBytes(op)
}
fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes {
epk.to_bytes()
}
fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option<Self::EphemeralPublicKey> {
EphemeralPublicKey::from_bytes(&ephemeral_key.0).into()
}
fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment {
note.commitment().into()
}
fn parse_note_plaintext_without_memo_ivk(
&self,
ivk: &Self::IncomingViewingKey,
plaintext: &[u8],
) -> Option<(Self::Note, Self::Recipient)> {
orchard_parse_note_plaintext_without_memo(self, plaintext, |diversifier| {
Some(DiversifiedTransmissionKey::derive(ivk, diversifier))
})
}
fn parse_note_plaintext_without_memo_ovk(
&self,
pk_d: &Self::DiversifiedTransmissionKey,
esk: &Self::EphemeralSecretKey,
ephemeral_key: &EphemeralKeyBytes,
plaintext: &NotePlaintextBytes,
) -> Option<(Self::Note, Self::Recipient)> {
orchard_parse_note_plaintext_without_memo(self, &plaintext.0, |diversifier| {
if esk
.derive_public(diversify_hash(diversifier.as_array()))
.to_bytes()
.0
== ephemeral_key.0
{
Some(*pk_d)
} else {
None
}
})
}
fn extract_memo(&self, plaintext: &NotePlaintextBytes) -> Self::Memo {
plaintext.0[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]
.try_into()
.unwrap()
}
fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option<Self::DiversifiedTransmissionKey> {
DiversifiedTransmissionKey::from_bytes(out_plaintext.0[0..32].try_into().unwrap()).into()
}
fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option<Self::EphemeralSecretKey> {
EphemeralSecretKey::from_bytes(out_plaintext.0[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())
.into()
}
}
impl BatchDomain for OrchardDomain {
fn batch_kdf<'a>(
items: impl Iterator<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
) -> Vec<Option<Self::SymmetricKey>> {
let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip();
SharedSecret::batch_to_affine(shared_secrets)
.zip(ephemeral_keys.into_iter())
.map(|(secret, ephemeral_key)| {
secret.map(|dhsecret| SharedSecret::kdf_orchard_inner(dhsecret, ephemeral_key))
})
.collect()
}
}
/// Implementation of in-band secret distribution for Orchard bundles.
pub type OrchardNoteEncryption = zcash_note_encryption::NoteEncryption<OrchardDomain>;
impl<T> ShieldedOutput<OrchardDomain, ENC_CIPHERTEXT_SIZE> for Action<T> {
fn ephemeral_key(&self) -> EphemeralKeyBytes {
EphemeralKeyBytes(self.encrypted_note().epk_bytes)
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmx().to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
&self.encrypted_note().enc_ciphertext
}
}
/// A compact Action for light clients.
pub struct CompactAction {
ephemeral_key: EphemeralKeyBytes,
cmx: ExtractedNoteCommitment,
enc_ciphertext: [u8; 52],
}
impl fmt::Debug for CompactAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CompactAction")
}
}
impl<T> From<&Action<T>> for CompactAction {
fn from(action: &Action<T>) -> Self {
CompactAction {
ephemeral_key: action.ephemeral_key(),
cmx: *action.cmx(),
enc_ciphertext: action.encrypted_note().enc_ciphertext[..52]
.try_into()
.unwrap(),
}
}
}
impl ShieldedOutput<OrchardDomain, COMPACT_NOTE_SIZE> for CompactAction {
fn ephemeral_key(&self) -> EphemeralKeyBytes {
EphemeralKeyBytes(self.ephemeral_key.0)
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmx.to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
&self.enc_ciphertext
}
}
#[cfg(test)]
mod tests {
use rand::rngs::OsRng;
use zcash_note_encryption::{
try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk,
EphemeralKeyBytes,
};
use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption};
use crate::{
bundle::Action,
keys::{
DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey,
OutgoingViewingKey,
},
note::{ExtractedNoteCommitment, Nullifier, RandomSeed, TransmittedNoteCiphertext},
primitives::redpallas,
value::{NoteValue, ValueCommitment},
Address, Note,
};
#[test]
fn test_vectors() {
let test_vectors = crate::test_vectors::note_encryption::test_vectors();
for tv in test_vectors {
//
// Load the test vector components
//
// Recipient key material
let ivk = IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap();
let ovk = OutgoingViewingKey::from(tv.ovk);
let d = Diversifier::from_bytes(tv.default_d);
let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap();
// Received Action
let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap();
let rho = Nullifier::from_bytes(&tv.rho).unwrap();
let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap();
let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap();
let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key);
// Details about the expected note
let value = NoteValue::from_raw(tv.v);
let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap();
//
// Test the individual components
//
let shared_secret = esk.agree(&pk_d);
assert_eq!(shared_secret.to_bytes(), tv.shared_secret);
let k_enc = shared_secret.kdf_orchard(&ephemeral_key);
assert_eq!(k_enc.as_bytes(), tv.k_enc);
let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key);
assert_eq!(ock.as_ref(), tv.ock);
let recipient = Address::from_parts(d, pk_d);
let note = Note::from_parts(recipient, value, rho, rseed);
assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx);
let action = Action::from_parts(
// rho is the nullifier in the receiving Action.
rho,
// We don't need a valid rk for this test.
redpallas::VerificationKey::dummy(),
cmx,
TransmittedNoteCiphertext {
epk_bytes: ephemeral_key.0,
enc_ciphertext: tv.c_enc,
out_ciphertext: tv.c_out,
},
cv_net.clone(),
(),
);
//
// Test decryption
// (Tested first because it only requires immutable references.)
//
let domain = OrchardDomain { rho };
match try_note_decryption(&domain, &ivk, &action) {
Some((decrypted_note, decrypted_to, decrypted_memo)) => {
assert_eq!(decrypted_note, note);
assert_eq!(decrypted_to, recipient);
assert_eq!(&decrypted_memo[..], &tv.memo[..]);
}
None => panic!("Note decryption failed"),
}
match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) {
Some((decrypted_note, decrypted_to)) => {
assert_eq!(decrypted_note, note);
assert_eq!(decrypted_to, recipient);
}
None => panic!("Compact note decryption failed"),
}
match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) {
Some((decrypted_note, decrypted_to, decrypted_memo)) => {
assert_eq!(decrypted_note, note);
assert_eq!(decrypted_to, recipient);
assert_eq!(&decrypted_memo[..], &tv.memo[..]);
}
None => panic!("Output recovery failed"),
}
//
// Test encryption
//
let ne = OrchardNoteEncryption::new_with_esk(esk, Some(ovk), note, recipient, tv.memo);
assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]);
assert_eq!(
&ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..],
&tv.c_out[..]
);
}
}
}

View File

@ -5,5 +5,4 @@
// - EphemeralSecretKey
pub mod poseidon;
pub mod redpallas;
pub mod sinsemilla;

View File

@ -1,221 +0,0 @@
//! A minimal RedPallas implementation for use in Zcash.
use std::cmp::{Ord, Ordering, PartialOrd};
use std::convert::{TryFrom, TryInto};
use pasta_curves::pallas;
use rand::{CryptoRng, RngCore};
pub use reddsa::batch;
#[cfg(test)]
use rand::rngs::OsRng;
/// A RedPallas signature type.
pub trait SigType: reddsa::SigType + private::Sealed {}
/// A type variable corresponding to an Orchard spend authorization signature.
pub type SpendAuth = reddsa::orchard::SpendAuth;
impl SigType for SpendAuth {}
/// A type variable corresponding to an Orchard binding signature.
pub type Binding = reddsa::orchard::Binding;
impl SigType for Binding {}
/// A RedPallas signing key.
#[derive(Debug)]
pub struct SigningKey<T: SigType>(reddsa::SigningKey<T>);
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
fn from(sk: SigningKey<T>) -> [u8; 32] {
sk.0.into()
}
}
impl<T: SigType> From<&SigningKey<T>> for [u8; 32] {
fn from(sk: &SigningKey<T>) -> [u8; 32] {
sk.0.into()
}
}
impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
type Error = reddsa::Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
bytes.try_into().map(SigningKey)
}
}
impl SigningKey<SpendAuth> {
/// Randomizes this signing key with the given `randomizer`.
///
/// Randomization is only supported for `SpendAuth` keys.
pub fn randomize(&self, randomizer: &pallas::Scalar) -> Self {
SigningKey(self.0.randomize(randomizer))
}
}
impl<T: SigType> SigningKey<T> {
/// Creates a signature of type `T` on `msg` using this `SigningKey`.
pub fn sign<R: RngCore + CryptoRng>(&self, rng: R, msg: &[u8]) -> Signature<T> {
Signature(self.0.sign(rng, msg))
}
}
/// A RedPallas verification key.
#[derive(Clone, Debug)]
pub struct VerificationKey<T: SigType>(reddsa::VerificationKey<T>);
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
fn from(vk: VerificationKey<T>) -> [u8; 32] {
vk.0.into()
}
}
impl<T: SigType> From<&VerificationKey<T>> for [u8; 32] {
fn from(vk: &VerificationKey<T>) -> [u8; 32] {
vk.0.into()
}
}
impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
type Error = reddsa::Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
bytes.try_into().map(VerificationKey)
}
}
impl<'a, T: SigType> From<&'a SigningKey<T>> for VerificationKey<T> {
fn from(sk: &'a SigningKey<T>) -> VerificationKey<T> {
VerificationKey((&sk.0).into())
}
}
impl<T: SigType> PartialEq for VerificationKey<T> {
fn eq(&self, other: &Self) -> bool {
<[u8; 32]>::from(self).eq(&<[u8; 32]>::from(other))
}
}
impl<T: SigType> Eq for VerificationKey<T> {}
impl<T: SigType> PartialOrd for VerificationKey<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
<[u8; 32]>::from(self).partial_cmp(&<[u8; 32]>::from(other))
}
}
impl<T: SigType> Ord for VerificationKey<T> {
fn cmp(&self, other: &Self) -> Ordering {
<[u8; 32]>::from(self).cmp(&<[u8; 32]>::from(other))
}
}
impl VerificationKey<SpendAuth> {
/// Used in the note encryption tests.
#[cfg(test)]
pub(crate) fn dummy() -> Self {
VerificationKey((&reddsa::SigningKey::new(OsRng)).into())
}
/// Randomizes this verification key with the given `randomizer`.
///
/// Randomization is only supported for `SpendAuth` keys.
pub fn randomize(&self, randomizer: &pallas::Scalar) -> Self {
VerificationKey(self.0.randomize(randomizer))
}
/// Creates a batch validation item from a `SpendAuth` signature.
pub fn create_batch_item<M: AsRef<[u8]>>(
&self,
sig: Signature<SpendAuth>,
msg: &M,
) -> batch::Item<SpendAuth, Binding> {
batch::Item::from_spendauth(self.0.into(), sig.0, msg)
}
}
impl VerificationKey<Binding> {
/// Creates a batch validation item from a `Binding` signature.
pub fn create_batch_item<M: AsRef<[u8]>>(
&self,
sig: Signature<Binding>,
msg: &M,
) -> batch::Item<SpendAuth, Binding> {
batch::Item::from_binding(self.0.into(), sig.0, msg)
}
}
/// A RedPallas signature.
#[derive(Debug, Clone)]
pub struct Signature<T: SigType>(reddsa::Signature<T>);
impl<T: SigType> From<[u8; 64]> for Signature<T> {
fn from(bytes: [u8; 64]) -> Self {
Signature(bytes.into())
}
}
impl<T: SigType> From<&Signature<T>> for [u8; 64] {
fn from(sig: &Signature<T>) -> Self {
sig.0.into()
}
}
pub(crate) mod private {
use super::{Binding, SpendAuth};
pub trait Sealed {}
impl Sealed for SpendAuth {}
impl Sealed for Binding {}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use std::convert::TryFrom;
use proptest::prelude::*;
use super::{Binding, SigningKey, SpendAuth, VerificationKey};
prop_compose! {
/// Generate a uniformly distributed RedDSA spend authorization signing key.
pub fn arb_spendauth_signing_key()(
sk in prop::array::uniform32(prop::num::u8::ANY)
.prop_map(reddsa::SigningKey::try_from)
.prop_filter("Values must be parseable as valid signing keys", |r| r.is_ok())
) -> SigningKey<SpendAuth> {
SigningKey(sk.unwrap())
}
}
prop_compose! {
/// Generate a uniformly distributed RedDSA binding signing key.
pub fn arb_binding_signing_key()(
sk in prop::array::uniform32(prop::num::u8::ANY)
.prop_map(reddsa::SigningKey::try_from)
.prop_filter("Values must be parseable as valid signing keys", |r| r.is_ok())
) -> SigningKey<Binding> {
SigningKey(sk.unwrap())
}
}
prop_compose! {
/// Generate a uniformly distributed RedDSA spend authorization verification key.
pub fn arb_spendauth_verification_key()(sk in arb_spendauth_signing_key()) -> VerificationKey<SpendAuth> {
VerificationKey::from(&sk)
}
}
prop_compose! {
/// Generate a uniformly distributed RedDSA binding verification key.
pub fn arb_binding_verification_key()(sk in arb_binding_signing_key()) -> VerificationKey<Binding> {
VerificationKey::from(&sk)
}
}
}

View File

@ -1,338 +0,0 @@
//! Helper functions defined in the Zcash Protocol Specification.
use std::iter;
use std::ops::Deref;
use ff::{Field, PrimeField, PrimeFieldBits};
use group::GroupEncoding;
use group::{Curve, Group};
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
use pasta_curves::pallas;
use subtle::{ConditionallySelectable, CtOption};
use crate::{
constants::{
fixed_bases::COMMIT_IVK_PERSONALIZATION, util::gen_const_array,
KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE,
},
primitives::{poseidon, sinsemilla},
};
mod prf_expand;
pub(crate) use prf_expand::PrfExpand;
/// A Pallas point that is guaranteed to not be the identity.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct NonIdentityPallasPoint(pallas::Point);
impl Default for NonIdentityPallasPoint {
fn default() -> Self {
NonIdentityPallasPoint(pallas::Point::generator())
}
}
impl ConditionallySelectable for NonIdentityPallasPoint {
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
NonIdentityPallasPoint(pallas::Point::conditional_select(&a.0, &b.0, choice))
}
}
impl NonIdentityPallasPoint {
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Point::from_bytes(bytes)
.and_then(|p| CtOption::new(NonIdentityPallasPoint(p), !p.is_identity()))
}
}
impl Deref for NonIdentityPallasPoint {
type Target = pallas::Point;
fn deref(&self) -> &pallas::Point {
&self.0
}
}
/// An integer in [1..q_P].
#[derive(Clone, Copy, Debug)]
pub(crate) struct NonZeroPallasBase(pallas::Base);
impl Default for NonZeroPallasBase {
fn default() -> Self {
NonZeroPallasBase(pallas::Base::one())
}
}
impl ConditionallySelectable for NonZeroPallasBase {
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
NonZeroPallasBase(pallas::Base::conditional_select(&a.0, &b.0, choice))
}
}
impl NonZeroPallasBase {
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_repr(*bytes).and_then(NonZeroPallasBase::from_base)
}
pub(crate) fn to_bytes(&self) -> [u8; 32] {
self.0.to_repr()
}
pub(crate) fn from_base(b: pallas::Base) -> CtOption<Self> {
CtOption::new(NonZeroPallasBase(b), !b.is_zero())
}
/// Constructs a wrapper for a base field element that is guaranteed to be non-zero.
///
/// # Panics
///
/// Panics if `s.is_zero()`.
fn guaranteed(s: pallas::Base) -> Self {
assert!(!bool::from(s.is_zero()));
NonZeroPallasBase(s)
}
}
/// An integer in [1..r_P].
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct NonZeroPallasScalar(pallas::Scalar);
impl Default for NonZeroPallasScalar {
fn default() -> Self {
NonZeroPallasScalar(pallas::Scalar::one())
}
}
impl From<NonZeroPallasBase> for NonZeroPallasScalar {
fn from(s: NonZeroPallasBase) -> Self {
NonZeroPallasScalar::guaranteed(mod_r_p(s.0))
}
}
impl ConditionallySelectable for NonZeroPallasScalar {
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
NonZeroPallasScalar(pallas::Scalar::conditional_select(&a.0, &b.0, choice))
}
}
impl NonZeroPallasScalar {
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Scalar::from_repr(*bytes).and_then(NonZeroPallasScalar::from_scalar)
}
pub(crate) fn from_scalar(s: pallas::Scalar) -> CtOption<Self> {
CtOption::new(NonZeroPallasScalar(s), !s.is_zero())
}
/// Constructs a wrapper for a scalar field element that is guaranteed to be non-zero.
///
/// # Panics
///
/// Panics if `s.is_zero()`.
fn guaranteed(s: pallas::Scalar) -> Self {
assert!(!bool::from(s.is_zero()));
NonZeroPallasScalar(s)
}
}
impl Deref for NonZeroPallasScalar {
type Target = pallas::Scalar;
fn deref(&self) -> &pallas::Scalar {
&self.0
}
}
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base {
pallas::Base::from_bytes_wide(&x)
}
/// $\mathsf{ToScalar}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod r_P)$
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar {
pallas::Scalar::from_bytes_wide(&x)
}
/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
///
/// This requires no modular reduction because Pallas' base field is smaller than its
/// scalar field.
pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
pallas::Scalar::from_repr(x.to_repr()).unwrap()
}
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
pub(crate) fn commit_ivk(
ak: &pallas::Base,
nk: &pallas::Base,
rivk: &pallas::Scalar,
) -> CtOption<NonZeroPallasBase> {
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
// bits, which is equal to L_ORCHARD_BASE.
let domain = sinsemilla::CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
domain
.short_commit(
iter::empty()
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
rivk,
)
// Commit^ivk.Output is specified as [1..q_P] {⊥}. We get this from
// sinsemilla::CommitDomain::short_commit by construction:
// - 0 is not a valid x-coordinate for any Pallas point.
// - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which replaces
// the identity (which has no affine coordinates) with 0. but Sinsemilla is
// defined using incomplete addition, and thus will never produce the identity.
.map(NonZeroPallasBase::guaranteed)
}
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
///
/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint {
let hasher = pallas::Point::hash_to_curve(KEY_DIVERSIFICATION_PERSONALIZATION);
let pk_d = hasher(d);
// If the identity occurs, we replace it with a different fixed point.
// TODO: Replace the unwrap_or_else with a cached fixed point.
NonIdentityPallasPoint(CtOption::new(pk_d, !pk_d.is_identity()).unwrap_or_else(|| hasher(&[])))
}
/// $PRF^\mathsf{nfOrchard}(nk, \rho) := Poseidon(nk, \rho)$
///
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
///
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
.hash([nk, rho])
}
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
///
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
pub(crate) fn ka_orchard(
sk: &NonZeroPallasScalar,
b: &NonIdentityPallasPoint,
) -> NonIdentityPallasPoint {
let mut wnaf = group::Wnaf::new();
NonIdentityPallasPoint(wnaf.scalar(sk.deref()).base(*b.deref()))
}
/// Coordinate extractor for Pallas.
///
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
///
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base {
point
.to_affine()
.coordinates()
.map(|c| *c.x())
.unwrap_or_else(pallas::Base::zero)
}
/// Coordinate extractor for Pallas.
///
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
///
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
pub(crate) fn extract_p_bottom(point: CtOption<pallas::Point>) -> CtOption<pallas::Base> {
point.map(|p| extract_p(&p))
}
/// The field element representation of a u64 integer represented by
/// an L-bit little-endian bitstring.
pub fn lebs2ip_field<F: PrimeField, const L: usize>(bits: &[bool; L]) -> F {
F::from(lebs2ip::<L>(bits))
}
/// The u64 integer represented by an L-bit little-endian bitstring.
///
/// # Panics
///
/// Panics if the bitstring is longer than 64 bits.
pub fn lebs2ip<const L: usize>(bits: &[bool; L]) -> u64 {
assert!(L <= 64);
bits.iter()
.enumerate()
.fold(0u64, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
}
/// The sequence of bits representing a u64 in little-endian order.
///
/// # Panics
///
/// Panics if the expected length of the sequence `NUM_BITS` exceeds
/// 64.
pub fn i2lebsp<const NUM_BITS: usize>(int: u64) -> [bool; NUM_BITS] {
assert!(NUM_BITS <= 64);
gen_const_array(|mask: usize| (int & (1 << mask)) != 0)
}
#[cfg(test)]
mod tests {
use super::{i2lebsp, lebs2ip};
use group::Group;
use halo2::arithmetic::CurveExt;
use pasta_curves::pallas;
use rand::{rngs::OsRng, RngCore};
use std::convert::TryInto;
#[test]
fn diversify_hash_substitution() {
assert!(!bool::from(
pallas::Point::hash_to_curve("z.cash:Orchard-gd")(&[]).is_identity()
));
}
#[test]
fn lebs2ip_round_trip() {
let mut rng = OsRng;
{
let int = rng.next_u64();
assert_eq!(lebs2ip::<64>(&i2lebsp(int)), int);
}
assert_eq!(lebs2ip::<64>(&i2lebsp(0)), 0);
assert_eq!(
lebs2ip::<64>(&i2lebsp(0xFFFFFFFFFFFFFFFF)),
0xFFFFFFFFFFFFFFFF
);
}
#[test]
fn i2lebsp_round_trip() {
{
let bitstring = (0..64).map(|_| rand::random()).collect::<Vec<_>>();
assert_eq!(
i2lebsp::<64>(lebs2ip::<64>(&bitstring.clone().try_into().unwrap())).to_vec(),
bitstring
);
}
{
let bitstring = [false; 64];
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
}
{
let bitstring = [true; 64];
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
}
{
let bitstring = [];
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
}
}
}

View File

@ -1,73 +0,0 @@
use blake2b_simd::Params;
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
/// The set of domains in which $PRF^\mathsf{expand}$ is defined.
pub(crate) enum PrfExpand {
Esk,
Rcm,
OrchardAsk,
OrchardNk,
OrchardRivk,
Psi,
OrchardZip32Child,
OrchardDkOvk,
}
impl PrfExpand {
fn domain_separator(&self) -> u8 {
match self {
Self::Esk => 0x04,
Self::Rcm => 0x05,
Self::OrchardAsk => 0x06,
Self::OrchardNk => 0x07,
Self::OrchardRivk => 0x08,
Self::Psi => 0x09,
Self::OrchardZip32Child => 0x81,
Self::OrchardDkOvk => 0x82,
}
}
/// Expands the given secret key in this domain, with no additional data.
///
/// $PRF^\mathsf{expand}(sk, dst) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst)$
///
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
///
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
pub(crate) fn expand(self, sk: &[u8]) -> [u8; 64] {
self.with_ad_slices(sk, &[])
}
/// Expands the given secret key in this domain, with the given additional data.
///
/// $PRF^\mathsf{expand}(sk, dst, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst || t)$
///
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
///
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
pub(crate) fn with_ad(self, sk: &[u8], t: &[u8]) -> [u8; 64] {
self.with_ad_slices(sk, &[t])
}
/// Expands the given secret key in this domain, with additional data concatenated
/// from the given slices.
///
/// $PRF^\mathsf{expand}(sk, dst, a, b, ...) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst || a || b || ...)$
///
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
///
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
pub(crate) fn with_ad_slices(self, sk: &[u8], ts: &[&[u8]]) -> [u8; 64] {
let mut h = Params::new()
.hash_length(64)
.personal(PRF_EXPAND_PERSONALIZATION)
.to_state();
h.update(sk);
h.update(&[self.domain_separator()]);
for t in ts {
h.update(t);
}
*h.finalize().as_array()
}
}

View File

@ -1,4 +0,0 @@
pub(crate) mod commitment_tree;
pub(crate) mod keys;
pub(crate) mod merkle_path;
pub(crate) mod note_encryption;

View File

@ -1,176 +0,0 @@
pub(crate) struct TestVector {
pub empty_roots: [[u8; 32]; 33],
}
pub(crate) fn test_vectors() -> TestVector {
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_empty_roots.py
TestVector {
empty_roots: [
[
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
[
0xd1, 0xab, 0x25, 0x07, 0xc8, 0x09, 0xc2, 0x71, 0x3c, 0x00, 0x0f, 0x52, 0x5e, 0x9f,
0xbd, 0xcb, 0x06, 0xc9, 0x58, 0x38, 0x4e, 0x51, 0xb9, 0xcc, 0x7f, 0x79, 0x2d, 0xde,
0x6c, 0x97, 0xf4, 0x11,
],
[
0xc7, 0x41, 0x3f, 0x46, 0x14, 0xcd, 0x64, 0x04, 0x3a, 0xbb, 0xab, 0x7c, 0xc1, 0x09,
0x5c, 0x9b, 0xb1, 0x04, 0x23, 0x1c, 0xea, 0x89, 0xe2, 0xc3, 0xe0, 0xdf, 0x83, 0x76,
0x95, 0x56, 0xd0, 0x30,
],
[
0x21, 0x11, 0xfc, 0x39, 0x77, 0x53, 0xe5, 0xfd, 0x50, 0xec, 0x74, 0x81, 0x6d, 0xf2,
0x7d, 0x6a, 0xda, 0x7e, 0xd2, 0xa9, 0xac, 0x38, 0x16, 0xaa, 0xb2, 0x57, 0x3c, 0x8f,
0xac, 0x79, 0x42, 0x04,
],
[
0x80, 0x6a, 0xfb, 0xfe, 0xb4, 0x5c, 0x64, 0xd4, 0xf2, 0x38, 0x4c, 0x51, 0xef, 0xf3,
0x07, 0x64, 0xb8, 0x45, 0x99, 0xae, 0x56, 0xa7, 0xab, 0x3d, 0x4a, 0x46, 0xd9, 0xce,
0x3a, 0xea, 0xb4, 0x31,
],
[
0x87, 0x3e, 0x41, 0x57, 0xf2, 0xc0, 0xf0, 0xc6, 0x45, 0xe8, 0x99, 0x36, 0x00, 0x69,
0xfc, 0xc9, 0xd2, 0xed, 0x9b, 0xc1, 0x1b, 0xf5, 0x98, 0x27, 0xaf, 0x02, 0x30, 0xed,
0x52, 0xed, 0xab, 0x18,
],
[
0x27, 0xab, 0x13, 0x20, 0x95, 0x3a, 0xe1, 0xad, 0x70, 0xc8, 0xc1, 0x5a, 0x12, 0x53,
0xa0, 0xa8, 0x6f, 0xbc, 0x8a, 0x0a, 0xa3, 0x6a, 0x84, 0x20, 0x72, 0x93, 0xf8, 0xa4,
0x95, 0xff, 0xc4, 0x02,
],
[
0x4e, 0x14, 0x56, 0x3d, 0xf1, 0x91, 0xa2, 0xa6, 0x5b, 0x4b, 0x37, 0x11, 0x3b, 0x52,
0x30, 0x68, 0x05, 0x55, 0x05, 0x1b, 0x22, 0xd7, 0x4a, 0x8e, 0x1f, 0x1d, 0x70, 0x6f,
0x90, 0xf3, 0x13, 0x3b,
],
[
0xb3, 0xbb, 0xe4, 0xf9, 0x93, 0xd1, 0x8a, 0x0f, 0x4e, 0xb7, 0xf4, 0x17, 0x4b, 0x1d,
0x85, 0x55, 0xce, 0x33, 0x96, 0x85, 0x5d, 0x04, 0x67, 0x6f, 0x1c, 0xe4, 0xf0, 0x6d,
0xda, 0x07, 0x37, 0x1f,
],
[
0x4e, 0xf5, 0xbd, 0xe9, 0xc6, 0xf0, 0xd7, 0x6a, 0xeb, 0x9e, 0x27, 0xe9, 0x3f, 0xba,
0x28, 0xc6, 0x79, 0xdf, 0xcb, 0x99, 0x1c, 0xbc, 0xb8, 0x39, 0x5a, 0x2b, 0x57, 0x92,
0x4c, 0xbd, 0x17, 0x0e,
],
[
0xa3, 0xc0, 0x25, 0x68, 0xac, 0xeb, 0xf5, 0xca, 0x1e, 0xc3, 0x0d, 0x6a, 0x7d, 0x7c,
0xd2, 0x17, 0xa4, 0x7d, 0x6a, 0x1b, 0x83, 0x11, 0xbf, 0x94, 0x62, 0xa5, 0xf9, 0x39,
0xc6, 0xb7, 0x43, 0x07,
],
[
0x3e, 0xf9, 0xb3, 0x0b, 0xae, 0x61, 0x22, 0xda, 0x16, 0x05, 0xba, 0xd6, 0xec, 0x5d,
0x49, 0xb4, 0x1d, 0x4d, 0x40, 0xca, 0xa9, 0x6c, 0x1c, 0xf6, 0x30, 0x2b, 0x66, 0xc5,
0xd2, 0xd1, 0x0d, 0x39,
],
[
0x22, 0xae, 0x28, 0x00, 0xcb, 0x93, 0xab, 0xe6, 0x3b, 0x70, 0xc1, 0x72, 0xde, 0x70,
0x36, 0x2d, 0x98, 0x30, 0xe5, 0x38, 0x00, 0x39, 0x88, 0x84, 0xa7, 0xa6, 0x4f, 0xf6,
0x8e, 0xd9, 0x9e, 0x0b,
],
[
0x18, 0x71, 0x10, 0xd9, 0x26, 0x72, 0xc2, 0x4c, 0xed, 0xb0, 0x97, 0x9c, 0xdf, 0xc9,
0x17, 0xa6, 0x05, 0x3b, 0x31, 0x0d, 0x14, 0x5c, 0x03, 0x1c, 0x72, 0x92, 0xbb, 0x1d,
0x65, 0xb7, 0x66, 0x1b,
],
[
0x3f, 0x98, 0xad, 0xbe, 0x36, 0x4f, 0x14, 0x8b, 0x0c, 0xc2, 0x04, 0x2c, 0xaf, 0xc6,
0xbe, 0x11, 0x66, 0xfa, 0xe3, 0x90, 0x90, 0xab, 0x4b, 0x35, 0x4b, 0xfb, 0x62, 0x17,
0xb9, 0x64, 0x45, 0x3b,
],
[
0x63, 0xf8, 0xdb, 0xd1, 0x0d, 0xf9, 0x36, 0xf1, 0x73, 0x49, 0x73, 0xe0, 0xb3, 0xbd,
0x25, 0xf4, 0xed, 0x44, 0x05, 0x66, 0xc9, 0x23, 0x08, 0x59, 0x03, 0xf6, 0x96, 0xbc,
0x63, 0x47, 0xec, 0x0f,
],
[
0x21, 0x82, 0x16, 0x3e, 0xac, 0x40, 0x61, 0x88, 0x5a, 0x31, 0x35, 0x68, 0x14, 0x8d,
0xfa, 0xe5, 0x64, 0xe4, 0x78, 0x06, 0x6d, 0xcb, 0xe3, 0x89, 0xa0, 0xdd, 0xb1, 0xec,
0xb7, 0xf5, 0xdc, 0x34,
],
[
0xbd, 0x9d, 0xc0, 0x68, 0x19, 0x18, 0xa3, 0xf3, 0xf9, 0xcd, 0x1f, 0x9e, 0x06, 0xaa,
0x1a, 0xd6, 0x89, 0x27, 0xda, 0x63, 0xac, 0xc1, 0x3b, 0x92, 0xa2, 0x57, 0x8b, 0x27,
0x38, 0xa6, 0xd3, 0x31,
],
[
0xca, 0x2c, 0xed, 0x95, 0x3b, 0x7f, 0xb9, 0x5e, 0x3b, 0xa9, 0x86, 0x33, 0x3d, 0xa9,
0xe6, 0x9c, 0xd3, 0x55, 0x22, 0x3c, 0x92, 0x97, 0x31, 0x09, 0x4b, 0x6c, 0x21, 0x74,
0xc7, 0x63, 0x8d, 0x2e,
],
[
0x55, 0x35, 0x4b, 0x96, 0xb5, 0x6f, 0x9e, 0x45, 0xaa, 0xe1, 0xe0, 0x09, 0x4d, 0x71,
0xee, 0x24, 0x8d, 0xab, 0xf6, 0x68, 0x11, 0x77, 0x78, 0xbd, 0xc3, 0xc1, 0x9c, 0xa5,
0x33, 0x1a, 0x4e, 0x1a,
],
[
0x70, 0x97, 0xb0, 0x4c, 0x2a, 0xa0, 0x45, 0xa0, 0xde, 0xff, 0xca, 0xca, 0x41, 0xc5,
0xac, 0x92, 0xe6, 0x94, 0x46, 0x65, 0x78, 0xf5, 0x90, 0x9e, 0x72, 0xbb, 0x78, 0xd3,
0x33, 0x10, 0xf7, 0x05,
],
[
0xe8, 0x1d, 0x68, 0x21, 0xff, 0x81, 0x3b, 0xd4, 0x10, 0x86, 0x7a, 0x3f, 0x22, 0xe8,
0xe5, 0xcb, 0x7a, 0xc5, 0x59, 0x9a, 0x61, 0x0a, 0xf5, 0xc3, 0x54, 0xeb, 0x39, 0x28,
0x77, 0x36, 0x2e, 0x01,
],
[
0x15, 0x7d, 0xe8, 0x56, 0x7f, 0x7c, 0x49, 0x96, 0xb8, 0xc4, 0xfd, 0xc9, 0x49, 0x38,
0xfd, 0x80, 0x8c, 0x3b, 0x2a, 0x5c, 0xcb, 0x79, 0xd1, 0xa6, 0x38, 0x58, 0xad, 0xaa,
0x9a, 0x6d, 0xd8, 0x24,
],
[
0xfe, 0x1f, 0xce, 0x51, 0xcd, 0x61, 0x20, 0xc1, 0x2c, 0x12, 0x46, 0x95, 0xc4, 0xf9,
0x8b, 0x27, 0x59, 0x18, 0xfc, 0xea, 0xe6, 0xeb, 0x20, 0x98, 0x73, 0xed, 0x73, 0xfe,
0x73, 0x77, 0x5d, 0x0b,
],
[
0x1f, 0x91, 0x98, 0x29, 0x12, 0x01, 0x26, 0x69, 0xf7, 0x4d, 0x0c, 0xfa, 0x10, 0x30,
0xff, 0x37, 0xb1, 0x52, 0x32, 0x4e, 0x5b, 0x83, 0x46, 0xb3, 0x33, 0x5a, 0x0a, 0xae,
0xb6, 0x3a, 0x0a, 0x2d,
],
[
0x5d, 0xec, 0x15, 0xf5, 0x2a, 0xf1, 0x7d, 0xa3, 0x93, 0x13, 0x96, 0x18, 0x3c, 0xbb,
0xbf, 0xbe, 0xa7, 0xed, 0x95, 0x07, 0x14, 0x54, 0x0a, 0xec, 0x06, 0xc6, 0x45, 0xc7,
0x54, 0x97, 0x55, 0x22,
],
[
0xe8, 0xae, 0x2a, 0xd9, 0x1d, 0x46, 0x3b, 0xab, 0x75, 0xee, 0x94, 0x1d, 0x33, 0xcc,
0x58, 0x17, 0xb6, 0x13, 0xc6, 0x3c, 0xda, 0x94, 0x3a, 0x4c, 0x07, 0xf6, 0x00, 0x59,
0x1b, 0x08, 0x8a, 0x25,
],
[
0xd5, 0x3f, 0xde, 0xe3, 0x71, 0xce, 0xf5, 0x96, 0x76, 0x68, 0x23, 0xf4, 0xa5, 0x18,
0xa5, 0x83, 0xb1, 0x15, 0x82, 0x43, 0xaf, 0xe8, 0x97, 0x00, 0xf0, 0xda, 0x76, 0xda,
0x46, 0xd0, 0x06, 0x0f,
],
[
0x15, 0xd2, 0x44, 0x4c, 0xef, 0xe7, 0x91, 0x4c, 0x9a, 0x61, 0xe8, 0x29, 0xc7, 0x30,
0xec, 0xeb, 0x21, 0x62, 0x88, 0xfe, 0xe8, 0x25, 0xf6, 0xb3, 0xb6, 0x29, 0x8f, 0x6f,
0x6b, 0x6b, 0xd6, 0x2e,
],
[
0x4c, 0x57, 0xa6, 0x17, 0xa0, 0xaa, 0x10, 0xea, 0x7a, 0x83, 0xaa, 0x6b, 0x6b, 0x0e,
0xd6, 0x85, 0xb6, 0xa3, 0xd9, 0xe5, 0xb8, 0xfd, 0x14, 0xf5, 0x6c, 0xdc, 0x18, 0x02,
0x1b, 0x12, 0x25, 0x3f,
],
[
0x3f, 0xd4, 0x91, 0x5c, 0x19, 0xbd, 0x83, 0x1a, 0x79, 0x20, 0xbe, 0x55, 0xd9, 0x69,
0xb2, 0xac, 0x23, 0x35, 0x9e, 0x25, 0x59, 0xda, 0x77, 0xde, 0x23, 0x73, 0xf0, 0x6c,
0xa0, 0x14, 0xba, 0x27,
],
[
0x87, 0xd0, 0x63, 0xcd, 0x07, 0xee, 0x49, 0x44, 0x22, 0x2b, 0x77, 0x62, 0x84, 0x0e,
0xb9, 0x4c, 0x68, 0x8b, 0xec, 0x74, 0x3f, 0xa8, 0xbd, 0xf7, 0x71, 0x5c, 0x8f, 0xe2,
0x9f, 0x10, 0x4c, 0x2a,
],
[
0xae, 0x29, 0x35, 0xf1, 0xdf, 0xd8, 0xa2, 0x4a, 0xed, 0x7c, 0x70, 0xdf, 0x7d, 0xe3,
0xa6, 0x68, 0xeb, 0x7a, 0x49, 0xb1, 0x31, 0x98, 0x80, 0xdd, 0xe2, 0xbb, 0xd9, 0x03,
0x1a, 0xe5, 0xd8, 0x2f,
],
],
}
}

View File

@ -1,735 +0,0 @@
//! Test vectors for Orchard key components.
pub(crate) struct TestVector {
pub(crate) sk: [u8; 32],
pub(crate) ask: [u8; 32],
pub(crate) ak: [u8; 32],
pub(crate) nk: [u8; 32],
pub(crate) rivk: [u8; 32],
pub(crate) ivk: [u8; 32],
pub(crate) ovk: [u8; 32],
pub(crate) dk: [u8; 32],
pub(crate) default_d: [u8; 11],
pub(crate) default_pk_d: [u8; 32],
pub(crate) note_v: u64,
pub(crate) note_rho: [u8; 32],
pub(crate) note_rseed: [u8; 32],
pub(crate) note_cmx: [u8; 32],
pub(crate) note_nf: [u8; 32],
}
pub(crate) fn test_vectors() -> Vec<TestVector> {
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_key_components.py
vec![
TestVector {
sk: [
0x5d, 0x7a, 0x8f, 0x73, 0x9a, 0x2d, 0x9e, 0x94, 0x5b, 0x0c, 0xe1, 0x52, 0xa8, 0x04,
0x9e, 0x29, 0x4c, 0x4d, 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa, 0x2e, 0xf6,
0xee, 0x69, 0x21, 0x48,
],
ask: [
0x8e, 0xb8, 0xc4, 0x01, 0xc2, 0x87, 0xa6, 0xc1, 0x3a, 0x2c, 0x34, 0x5a, 0xd8, 0x21,
0x72, 0xd8, 0x6b, 0xe4, 0xa8, 0x85, 0x35, 0x25, 0xdb, 0x60, 0x2d, 0x14, 0xf6, 0x30,
0xf4, 0xe6, 0x1c, 0x17,
],
ak: [
0x74, 0x0b, 0xbe, 0x5d, 0x05, 0x80, 0xb2, 0xca, 0xd4, 0x30, 0x18, 0x0d, 0x02, 0xcc,
0x12, 0x8b, 0x9a, 0x14, 0x0d, 0x5e, 0x07, 0xc1, 0x51, 0x72, 0x1d, 0xc1, 0x6d, 0x25,
0xd4, 0xe2, 0x0f, 0x15,
],
nk: [
0x9f, 0x2f, 0x82, 0x67, 0x38, 0x94, 0x5a, 0xd0, 0x1f, 0x47, 0xf7, 0x0d, 0xb0, 0xc3,
0x67, 0xc2, 0x46, 0xc2, 0x0c, 0x61, 0xff, 0x55, 0x83, 0x94, 0x8c, 0x39, 0xde, 0xa9,
0x68, 0xfe, 0xfd, 0x1b,
],
rivk: [
0x02, 0x1c, 0xcf, 0x89, 0x60, 0x4f, 0x5f, 0x7c, 0xc6, 0xe0, 0x34, 0xb3, 0x2d, 0x33,
0x89, 0x08, 0xb8, 0x19, 0xfb, 0xe3, 0x25, 0xfe, 0xe6, 0x45, 0x8b, 0x56, 0xb4, 0xca,
0x71, 0xa7, 0xe4, 0x3d,
],
ivk: [
0x85, 0xc8, 0xb5, 0xcd, 0x1a, 0xc3, 0xec, 0x3a, 0xd7, 0x09, 0x21, 0x32, 0xf9, 0x7f,
0x01, 0x78, 0xb0, 0x75, 0xc8, 0x1a, 0x13, 0x9f, 0xd4, 0x60, 0xbb, 0xe0, 0xdf, 0xcd,
0x75, 0x51, 0x47, 0x24,
],
ovk: [
0xbc, 0xc7, 0x06, 0x5e, 0x59, 0x91, 0x0b, 0x35, 0x99, 0x3f, 0x59, 0x50, 0x5b, 0xe2,
0x09, 0xb1, 0x4b, 0xf0, 0x24, 0x88, 0x75, 0x0b, 0xbc, 0x8b, 0x1a, 0xcd, 0xcf, 0x10,
0x8c, 0x36, 0x20, 0x04,
],
dk: [
0x31, 0xd6, 0xa6, 0x85, 0xbe, 0x57, 0x0f, 0x9f, 0xaf, 0x3c, 0xa8, 0xb0, 0x52, 0xe8,
0x87, 0x84, 0x0b, 0x2c, 0x9f, 0x8d, 0x67, 0x22, 0x4c, 0xa8, 0x2a, 0xef, 0xb9, 0xe2,
0xee, 0x5b, 0xed, 0xaf,
],
default_d: [
0x8f, 0xf3, 0x38, 0x69, 0x71, 0xcb, 0x64, 0xb8, 0xe7, 0x78, 0x99,
],
default_pk_d: [
0x08, 0xdd, 0x8e, 0xbd, 0x7d, 0xe9, 0x2a, 0x68, 0xe5, 0x86, 0xa3, 0x4d, 0xb8, 0xfe,
0xa9, 0x99, 0xef, 0xd2, 0x01, 0x6f, 0xae, 0x76, 0x75, 0x0a, 0xfa, 0xe7, 0xee, 0x94,
0x16, 0x46, 0xbc, 0xb9,
],
note_v: 15643327852135767324,
note_rho: [
0x2c, 0xb5, 0xb4, 0x06, 0xed, 0x89, 0x85, 0xe1, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26,
0x97, 0xb0, 0xe4, 0xe4, 0xc7, 0x63, 0xcc, 0xb8, 0xf6, 0x76, 0x49, 0x5c, 0x22, 0x2f,
0x7f, 0xba, 0x1e, 0x31,
],
note_rseed: [
0xde, 0xfa, 0x3d, 0x5a, 0x57, 0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87,
0xd5, 0xfb, 0x1a, 0x38, 0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c, 0x3e, 0x0a, 0xd3, 0x36,
0x0c, 0x1d, 0x37, 0x10,
],
note_cmx: [
0x45, 0x02, 0xe3, 0x39, 0x90, 0x1e, 0x39, 0x77, 0x17, 0x83, 0x91, 0x67, 0xcb, 0xb4,
0x03, 0x7e, 0x0e, 0xcf, 0x68, 0x13, 0xb5, 0x1c, 0x81, 0xfe, 0x08, 0x5a, 0x7b, 0x78,
0x2f, 0x12, 0x42, 0x28,
],
note_nf: [
0x1b, 0x32, 0xed, 0xbb, 0xe4, 0xd1, 0x8f, 0x28, 0x87, 0x6d, 0xe2, 0x62, 0x51, 0x8a,
0xd3, 0x11, 0x22, 0x70, 0x1f, 0x8c, 0x0a, 0x52, 0xe9, 0x80, 0x47, 0xa3, 0x37, 0x87,
0x6e, 0x7e, 0xea, 0x19,
],
},
TestVector {
sk: [
0xac, 0xd2, 0x0b, 0x18, 0x3e, 0x31, 0xd4, 0x9f, 0x25, 0xc9, 0xa1, 0x38, 0xf4, 0x9b,
0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x51, 0xa7, 0xaf, 0x9d, 0xb6,
0x99, 0x0e, 0xd8, 0x3d,
],
ask: [
0x41, 0xd4, 0x7c, 0xc9, 0x63, 0x13, 0xb4, 0x82, 0x1d, 0xfc, 0x12, 0x96, 0x51, 0xc3,
0x13, 0x7f, 0x44, 0xd9, 0xca, 0xd1, 0x6b, 0x3d, 0xc0, 0x81, 0x33, 0xc3, 0xd2, 0xdf,
0x0d, 0x0c, 0x53, 0x20,
],
ak: [
0x6d, 0xe1, 0x34, 0x98, 0x30, 0xd6, 0x6d, 0x7b, 0x97, 0xfe, 0x23, 0x1f, 0xc7, 0xb0,
0x2a, 0xd6, 0x43, 0x23, 0x62, 0x9c, 0xfe, 0xd1, 0xe3, 0xaa, 0x24, 0xef, 0x05, 0x2f,
0x56, 0xe4, 0x00, 0x2a,
],
nk: [
0xa8, 0xb7, 0x3d, 0x97, 0x9b, 0x6e, 0xaa, 0xda, 0x89, 0x24, 0xbc, 0xbd, 0xc6, 0x3a,
0x9e, 0xf4, 0xe8, 0x73, 0x46, 0xf2, 0x30, 0xab, 0xa6, 0xbb, 0xe1, 0xe2, 0xb4, 0x3c,
0x5b, 0xea, 0x6b, 0x22,
],
rivk: [
0xda, 0xcb, 0x2f, 0x2a, 0x9c, 0xed, 0x36, 0x31, 0x71, 0x82, 0x1a, 0xaf, 0x5d, 0x8c,
0xd9, 0x02, 0xbc, 0x5e, 0x3a, 0x5a, 0x41, 0xfb, 0x51, 0xae, 0x61, 0xa9, 0xf0, 0x2d,
0xc8, 0x9d, 0x1d, 0x12,
],
ivk: [
0x56, 0x3a, 0x6d, 0xb6, 0x0c, 0x74, 0xc2, 0xdb, 0x08, 0x49, 0x2c, 0xba, 0xe3, 0xbb,
0x08, 0x3f, 0x1a, 0xea, 0xbf, 0xfb, 0xcf, 0x42, 0x55, 0x1d, 0x0a, 0xc6, 0x4f, 0x26,
0x90, 0x53, 0x67, 0x11,
],
ovk: [
0x71, 0xcd, 0x30, 0x64, 0x0f, 0xdb, 0x63, 0xf8, 0xd1, 0x30, 0x50, 0x29, 0xe9, 0x40,
0xe5, 0x3f, 0xd5, 0xec, 0x04, 0xa8, 0xcc, 0xad, 0x41, 0x95, 0x78, 0xc2, 0x42, 0xfe,
0xc0, 0x5b, 0x9a, 0xf7,
],
dk: [
0x9d, 0x9b, 0xd4, 0x45, 0x25, 0xe7, 0xae, 0x06, 0xb0, 0x3a, 0xe6, 0xd4, 0xae, 0xcd,
0xe6, 0xae, 0x09, 0x27, 0xa7, 0xc6, 0x67, 0xd5, 0xd9, 0xf8, 0x17, 0x6b, 0x54, 0x46,
0x95, 0xdf, 0xec, 0x11,
],
default_d: [
0x78, 0x07, 0xca, 0x65, 0x08, 0x58, 0x81, 0x4d, 0x50, 0x22, 0xa8,
],
default_pk_d: [
0x3d, 0x3d, 0xe4, 0xd5, 0x2c, 0x77, 0xfd, 0x0b, 0x63, 0x0a, 0x40, 0xdc, 0x38, 0x21,
0x24, 0x87, 0xb2, 0xff, 0x6e, 0xee, 0xf5, 0x6d, 0x8c, 0x6a, 0x61, 0x63, 0xe8, 0x54,
0xaf, 0xf0, 0x41, 0x89,
],
note_v: 4481649511318637270,
note_rho: [
0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, 0x32, 0x0d,
0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, 0xdf, 0x65, 0x8d, 0x24, 0xae, 0x67, 0xc2,
0x2c, 0x8d, 0x13, 0x09,
],
note_rseed: [
0x13, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76, 0xd3, 0x8d, 0x47, 0xf1,
0xe1, 0x91, 0xe0, 0x0c, 0x7a, 0x1d, 0x48, 0xaf, 0x04, 0x68, 0x27, 0x59, 0x1e, 0x97,
0x33, 0xa9, 0x7f, 0xa6,
],
note_cmx: [
0xc7, 0xad, 0x79, 0x4c, 0x56, 0x3e, 0x32, 0xca, 0xd4, 0x7d, 0x47, 0xdc, 0xda, 0x78,
0x84, 0x69, 0x28, 0x48, 0xdc, 0xe2, 0x9b, 0xa4, 0xfe, 0xbd, 0x93, 0x20, 0x2b, 0x73,
0x05, 0xf9, 0x03, 0x00,
],
note_nf: [
0x2c, 0xf0, 0x67, 0xbc, 0x21, 0xd6, 0x63, 0x20, 0xe5, 0x1b, 0x9f, 0xbd, 0xc8, 0xae,
0x03, 0x1c, 0x2c, 0x96, 0x37, 0x3d, 0xb4, 0x3b, 0x7b, 0x1a, 0x45, 0x05, 0x6c, 0x00,
0xc6, 0x5d, 0x43, 0x20,
],
},
TestVector {
sk: [
0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, 0xe6, 0x9c,
0xe8, 0xfc, 0x1b, 0xe4, 0xaa, 0xc0, 0x0f, 0xf2, 0x71, 0x1e, 0xbd, 0x93, 0x1d, 0xe5,
0x18, 0x85, 0x68, 0x78,
],
ask: [
0xce, 0x8b, 0x65, 0xa7, 0x23, 0x65, 0x11, 0xb2, 0xea, 0xf1, 0x9f, 0x72, 0xa3, 0xd6,
0xdb, 0x7d, 0x06, 0x2b, 0x66, 0xf5, 0x16, 0x30, 0x7d, 0x19, 0x87, 0x06, 0xe5, 0xf6,
0x92, 0x8e, 0x16, 0x15,
],
ak: [
0xef, 0xa5, 0xf1, 0xde, 0xbe, 0xea, 0xd0, 0x94, 0x0a, 0x61, 0x9c, 0xe0, 0x01, 0x7b,
0xed, 0xb4, 0x26, 0x65, 0x7b, 0x2d, 0x07, 0x40, 0x66, 0x64, 0xd8, 0x95, 0x31, 0x2e,
0xa1, 0xc3, 0xb3, 0x34,
],
nk: [
0x04, 0x51, 0x4e, 0xa0, 0x48, 0xb9, 0x43, 0x63, 0xde, 0xa7, 0xcb, 0x3b, 0xe8, 0xd6,
0x25, 0x82, 0xac, 0x52, 0x92, 0x2e, 0x08, 0x65, 0xf6, 0x62, 0x74, 0x3b, 0x05, 0xea,
0xe8, 0x71, 0x5f, 0x17,
],
rivk: [
0x2a, 0x32, 0x8f, 0x99, 0x4f, 0x6e, 0x5a, 0xd2, 0x9c, 0xa8, 0x11, 0xed, 0x34, 0x49,
0x68, 0xea, 0x2c, 0xfc, 0x3f, 0xd2, 0x31, 0x03, 0x0e, 0x37, 0xbb, 0xd5, 0x6d, 0xb4,
0x26, 0x40, 0x23, 0x1c,
],
ivk: [
0x60, 0x9e, 0xcb, 0xc3, 0xd8, 0xce, 0xe3, 0xbe, 0x2b, 0x2a, 0x23, 0x62, 0x95, 0x1f,
0x58, 0xb7, 0x44, 0x82, 0xad, 0xfa, 0xee, 0xe1, 0xc4, 0x0f, 0x94, 0x03, 0x04, 0x40,
0xf5, 0x58, 0xaa, 0x30,
],
ovk: [
0xdf, 0xd3, 0x0f, 0x62, 0xaa, 0x31, 0x9c, 0x6f, 0x53, 0xe2, 0x4c, 0x1f, 0x48, 0xc1,
0xde, 0x96, 0x1b, 0x90, 0x01, 0xcb, 0x98, 0x8b, 0x80, 0xb3, 0xed, 0xa2, 0x44, 0xfc,
0xfe, 0xb2, 0x5f, 0x83,
],
dk: [
0x23, 0x6b, 0xc3, 0xf3, 0xd0, 0x2f, 0x96, 0x02, 0x80, 0xee, 0xde, 0xde, 0x10, 0x8d,
0x36, 0x85, 0x04, 0x9f, 0x23, 0x9a, 0xa6, 0x7c, 0x48, 0x55, 0x8f, 0x7c, 0x01, 0xd3,
0xfd, 0x46, 0x9e, 0xcd,
],
default_d: [
0x64, 0x24, 0xf7, 0x1a, 0x3a, 0xd1, 0x97, 0x42, 0x64, 0x98, 0xf4,
],
default_pk_d: [
0xec, 0xcb, 0x6a, 0x57, 0x80, 0x20, 0x42, 0x37, 0x98, 0x72, 0x32, 0xbc, 0x09, 0x8f,
0x89, 0xac, 0xc4, 0x75, 0xc3, 0xf7, 0x4b, 0xd6, 0x9e, 0x2f, 0x35, 0xd4, 0x47, 0x36,
0xf4, 0x8f, 0x3c, 0x14,
],
note_v: 14496603531126387959,
note_rho: [
0x32, 0xb4, 0xf4, 0x73, 0xf4, 0x68, 0xa0, 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88,
0x0d, 0x78, 0x0c, 0xb0, 0x7f, 0xcf, 0xaa, 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5,
0x9a, 0x4a, 0x15, 0x3d,
],
note_rseed: [
0x88, 0x2d, 0x2b, 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, 0x94, 0x94, 0xc6, 0xac, 0x89,
0x3c, 0x49, 0x72, 0x38, 0x33, 0xec, 0x89, 0x26, 0xc1, 0x03, 0x95, 0x86, 0xa7, 0xaf,
0xcf, 0x4a, 0x0d, 0x9c,
],
note_cmx: [
0x03, 0xce, 0x20, 0xce, 0xa1, 0x94, 0xb7, 0x55, 0x9a, 0x8a, 0x90, 0x47, 0x1d, 0x28,
0xa3, 0xc0, 0x53, 0xc3, 0x72, 0x0a, 0xd4, 0x9f, 0x40, 0xd2, 0x7c, 0x2d, 0xcc, 0xe3,
0x35, 0x00, 0x56, 0x16,
],
note_nf: [
0x16, 0xfa, 0x2c, 0x34, 0x97, 0xfc, 0x09, 0xad, 0x90, 0xdd, 0x34, 0x92, 0x02, 0xa2,
0x4b, 0x69, 0x89, 0x2d, 0xc8, 0x06, 0x29, 0xb2, 0xd1, 0xbf, 0xeb, 0xaf, 0x41, 0x70,
0x8f, 0x0f, 0xb1, 0x0c,
],
},
TestVector {
sk: [
0x73, 0x1e, 0x98, 0x5d, 0x99, 0x58, 0x9c, 0x8b, 0xb8, 0x38, 0xe8, 0xaa, 0xf7, 0x45,
0x53, 0x3e, 0xd9, 0xe8, 0xae, 0x3a, 0x1c, 0xd0, 0x74, 0xa5, 0x1a, 0x20, 0xda, 0x8a,
0xba, 0x18, 0xd1, 0xdb,
],
ask: [
0x42, 0x6a, 0x78, 0x44, 0xf3, 0x05, 0xb9, 0xd4, 0xe0, 0x7e, 0xa5, 0x2a, 0x39, 0x00,
0x1c, 0x9b, 0x33, 0x6c, 0xfc, 0x0d, 0x6f, 0xa1, 0x5e, 0xf3, 0xd1, 0x1c, 0x3d, 0x7b,
0x74, 0xf0, 0x8c, 0x2d,
],
ak: [
0xb1, 0xe0, 0xac, 0xbc, 0x69, 0xbf, 0x37, 0x7b, 0x85, 0xab, 0xf0, 0xf5, 0xa1, 0x0b,
0xe7, 0x2c, 0x3b, 0x64, 0x00, 0x06, 0xff, 0x08, 0x50, 0x52, 0x80, 0xe4, 0xf0, 0x0f,
0xad, 0xf7, 0x63, 0x28,
],
nk: [
0xcf, 0x36, 0xad, 0x6a, 0x06, 0x6c, 0xd2, 0x13, 0xe1, 0xd7, 0x67, 0xab, 0x07, 0x1d,
0xc1, 0x16, 0x78, 0x85, 0xc4, 0x16, 0x8b, 0xc2, 0xe2, 0x17, 0x54, 0x48, 0x56, 0x3a,
0xd1, 0x3f, 0x33, 0x3d,
],
rivk: [
0xc4, 0x1b, 0xba, 0xd3, 0x51, 0x05, 0xa8, 0x03, 0x14, 0xb7, 0x96, 0x24, 0xb6, 0x75,
0x24, 0x12, 0x20, 0xb3, 0x31, 0xf1, 0x25, 0x92, 0x61, 0x7b, 0xdb, 0x70, 0x5b, 0xfc,
0xce, 0x72, 0xae, 0x38,
],
ivk: [
0xf7, 0x9f, 0xe8, 0x02, 0xe4, 0xd2, 0x43, 0x07, 0xa6, 0xaa, 0xf8, 0x5d, 0x19, 0xf5,
0xe0, 0x83, 0x37, 0x40, 0xba, 0xe5, 0x98, 0xdc, 0x7c, 0x88, 0x0a, 0xc6, 0x09, 0x63,
0x1d, 0xe1, 0x58, 0x19,
],
ovk: [
0xf9, 0x63, 0x66, 0xbc, 0x6e, 0xab, 0xd2, 0x32, 0x54, 0x9e, 0xbb, 0x43, 0xb4, 0xed,
0x6f, 0xd8, 0x1d, 0x33, 0x03, 0x73, 0xc5, 0xb5, 0x66, 0x90, 0x4e, 0x9a, 0xf1, 0x1a,
0x6b, 0xab, 0x8d, 0x77,
],
dk: [
0x80, 0x3e, 0x34, 0x85, 0x73, 0x02, 0x2b, 0xf8, 0x93, 0x2f, 0x23, 0xee, 0x7a, 0x32,
0x5e, 0xa2, 0x83, 0x87, 0x9c, 0x65, 0x24, 0x12, 0xb8, 0x60, 0x6b, 0xe3, 0x19, 0x8c,
0x4b, 0x78, 0x2c, 0x47,
],
default_d: [
0xdb, 0x8c, 0x30, 0x55, 0x24, 0xbc, 0x0d, 0xea, 0xa8, 0x5d, 0x97,
],
default_pk_d: [
0x04, 0xea, 0x8c, 0x13, 0x20, 0xff, 0xbb, 0xad, 0xfe, 0x96, 0xf0, 0xc6, 0xff, 0x16,
0xb6, 0x07, 0x11, 0x1b, 0x55, 0x83, 0xbf, 0xb6, 0xf1, 0xea, 0x45, 0x27, 0x5e, 0xf2,
0xaa, 0x2d, 0x87, 0x9b,
],
note_v: 6792346249443327211,
note_rho: [
0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96, 0xbc,
0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7,
0x1a, 0x02, 0xaf, 0x11,
],
note_rseed: [
0x7d, 0x41, 0x7a, 0xdb, 0x3d, 0x15, 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50,
0x0c, 0x6b, 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee,
0xcc, 0x40, 0xa9, 0x8d,
],
note_cmx: [
0xa9, 0xb1, 0x1b, 0xaf, 0x30, 0x34, 0xb6, 0x5c, 0x64, 0x24, 0x84, 0x1b, 0xfe, 0x02,
0x3f, 0x8e, 0xda, 0x13, 0x13, 0xc3, 0x0a, 0xa2, 0x7d, 0xe9, 0x2e, 0x21, 0xa1, 0x08,
0x31, 0x6e, 0x82, 0x19,
],
note_nf: [
0x72, 0xd6, 0x30, 0x89, 0x60, 0x35, 0x1f, 0x7b, 0x26, 0xfa, 0x64, 0x60, 0x3f, 0xe4,
0xdf, 0xd8, 0x67, 0xbd, 0x5e, 0xb3, 0x67, 0xba, 0x2b, 0x7c, 0xa4, 0x91, 0xc9, 0x23,
0xc0, 0xea, 0xd2, 0x22,
],
},
TestVector {
sk: [
0x5f, 0x29, 0x35, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, 0xfd, 0xbb, 0x5d, 0x47,
0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, 0xb7, 0xfa,
0xe2, 0xdb, 0x58, 0x71,
],
ask: [
0x11, 0x80, 0x73, 0x28, 0x51, 0x64, 0xe6, 0x55, 0x73, 0x58, 0xfb, 0xc4, 0x1a, 0x81,
0x35, 0xcb, 0x06, 0x2f, 0x86, 0x76, 0xcb, 0x61, 0xf9, 0xaa, 0x52, 0xd1, 0x9a, 0x09,
0xfa, 0xc5, 0x58, 0x02,
],
ak: [
0x0d, 0x26, 0x2d, 0xe3, 0x60, 0x94, 0x33, 0xfe, 0x5b, 0x7c, 0x86, 0x2b, 0xc4, 0x8e,
0xf5, 0x6d, 0x83, 0x20, 0x09, 0xf7, 0x24, 0x2e, 0x1f, 0x7c, 0x77, 0x0a, 0x12, 0x24,
0x1d, 0xfa, 0x28, 0x07,
],
nk: [
0x51, 0xba, 0xf3, 0x33, 0xcf, 0xf1, 0xf2, 0xd0, 0xc7, 0xe3, 0xcf, 0xf4, 0xd3, 0x01,
0x29, 0x9d, 0xc1, 0xef, 0xe9, 0x83, 0x00, 0x31, 0x4a, 0x54, 0x19, 0x38, 0x02, 0x9b,
0x45, 0xcc, 0x15, 0x21,
],
rivk: [
0x22, 0x8f, 0xeb, 0x79, 0x21, 0x98, 0x73, 0xc7, 0xa7, 0x60, 0x6e, 0x52, 0x97, 0x3c,
0x85, 0xf4, 0x60, 0x46, 0x5a, 0x60, 0x59, 0x08, 0x39, 0x19, 0xed, 0x73, 0xeb, 0x80,
0x5c, 0x11, 0x83, 0x01,
],
ivk: [
0x76, 0xf4, 0x9c, 0xf8, 0xa3, 0x19, 0x21, 0x85, 0x61, 0x6a, 0x9a, 0x0d, 0xa0, 0xc7,
0x6e, 0xc2, 0xc2, 0x75, 0x61, 0x59, 0xbc, 0xe1, 0x86, 0xa1, 0x86, 0x2b, 0x6e, 0x6e,
0x59, 0x44, 0x2d, 0x11,
],
ovk: [
0xeb, 0x72, 0xb6, 0xc3, 0x1e, 0x83, 0x7f, 0xd8, 0x37, 0xaa, 0xcb, 0x61, 0xfa, 0xba,
0xce, 0x75, 0xa1, 0x9d, 0xd9, 0xdd, 0x5b, 0x4b, 0x3a, 0x3e, 0xe7, 0x23, 0xc1, 0x4d,
0xa7, 0x7b, 0x4b, 0xe8,
],
dk: [
0xee, 0x19, 0xf8, 0xdd, 0xd9, 0xda, 0x06, 0x34, 0x24, 0x51, 0x43, 0xc4, 0xb4, 0x3a,
0xfc, 0x7d, 0x78, 0xc5, 0x49, 0xc8, 0x20, 0x54, 0xa9, 0xd8, 0x40, 0x07, 0xb5, 0x62,
0x17, 0xdb, 0xfd, 0xd6,
],
default_d: [
0xaa, 0xe3, 0x6e, 0x09, 0x4d, 0xe0, 0x7b, 0xc1, 0x6f, 0x89, 0x8e,
],
default_pk_d: [
0xb6, 0x53, 0x3d, 0xcb, 0xff, 0xf0, 0xf6, 0xc1, 0xce, 0xef, 0xa8, 0x47, 0x99, 0xbd,
0xa3, 0xde, 0x73, 0x34, 0x32, 0x6c, 0xcd, 0x65, 0xf7, 0xce, 0x92, 0xff, 0x3d, 0x9e,
0x6e, 0x1f, 0x14, 0x0b,
],
note_v: 4079549063511228677,
note_rho: [
0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, 0x26, 0xc6, 0xcb, 0x4c, 0xd4, 0xb0, 0xf7, 0xf5,
0xaa, 0x2a, 0x4f, 0x5a, 0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15, 0x40, 0x6f, 0x2f, 0xdd,
0x2a, 0xfa, 0x73, 0x3f,
],
note_rseed: [
0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, 0x2a, 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee,
0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, 0x79, 0xf8, 0x80, 0x08, 0xe3, 0x15, 0xdc, 0x7d,
0x83, 0x88, 0xe7, 0x6c,
],
note_cmx: [
0x0f, 0xfb, 0xca, 0x1d, 0x59, 0x21, 0xfa, 0x0a, 0x8c, 0x51, 0x16, 0xae, 0x13, 0x7e,
0x37, 0xf2, 0xc1, 0x18, 0xd5, 0x21, 0x25, 0x62, 0x8d, 0x8a, 0x3f, 0x41, 0x2c, 0xe0,
0xe6, 0x53, 0x0e, 0x04,
],
note_nf: [
0xe6, 0x2b, 0x8e, 0xd8, 0x35, 0x40, 0x14, 0x6c, 0xd2, 0x3c, 0xac, 0x74, 0xee, 0xd7,
0xd7, 0x73, 0xd8, 0x02, 0x24, 0xa5, 0xaa, 0x30, 0xd6, 0x8e, 0x35, 0x57, 0x2e, 0xe8,
0x83, 0xd1, 0xb7, 0x04,
],
},
TestVector {
sk: [
0x17, 0x82, 0xfd, 0x27, 0x95, 0xd1, 0x8a, 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59,
0xcc, 0x97, 0x48, 0x9c, 0xe7, 0x57, 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23,
0x9c, 0xfb, 0xdf, 0x73,
],
ask: [
0xf6, 0xef, 0x32, 0x8d, 0x24, 0x76, 0x1d, 0x6d, 0x3c, 0xcd, 0x25, 0xd4, 0x71, 0x96,
0xe8, 0x10, 0x9c, 0x03, 0x8f, 0xe1, 0x7c, 0x59, 0xa7, 0xf0, 0x5b, 0x98, 0xd6, 0x6b,
0xeb, 0xc6, 0x41, 0x24,
],
ak: [
0xd1, 0x17, 0x87, 0xca, 0x58, 0x2f, 0x94, 0x8e, 0x45, 0x07, 0x18, 0xb3, 0x69, 0x98,
0xdf, 0x28, 0xbb, 0x0f, 0x10, 0x21, 0xea, 0x84, 0x3f, 0x86, 0x7f, 0x8a, 0x17, 0x0f,
0x5c, 0x33, 0x90, 0x1f,
],
nk: [
0x9e, 0x99, 0x7d, 0x9d, 0x26, 0x97, 0x87, 0x26, 0x8e, 0x09, 0x2a, 0x7c, 0x85, 0x41,
0x7d, 0xa5, 0x30, 0xea, 0x42, 0xfa, 0xc6, 0x68, 0xa7, 0x49, 0xaf, 0x55, 0xdf, 0xb7,
0x1c, 0xdb, 0xbe, 0x09,
],
rivk: [
0x13, 0x6c, 0x6f, 0xe2, 0xe2, 0xb7, 0x9c, 0x51, 0x56, 0xdb, 0x50, 0x47, 0xd8, 0xd5,
0xe7, 0x95, 0xdf, 0xc0, 0xbd, 0xc0, 0x88, 0x08, 0x53, 0xa4, 0x4a, 0xdb, 0x73, 0x92,
0xc0, 0x2f, 0x94, 0x1b,
],
ivk: [
0x02, 0x8b, 0x64, 0x05, 0x64, 0xb2, 0x49, 0x05, 0xde, 0x92, 0x92, 0xba, 0x5b, 0x98,
0x10, 0xad, 0xdd, 0x86, 0xbe, 0xd0, 0xfb, 0x3b, 0x2d, 0x6b, 0x37, 0xf2, 0x6d, 0xd2,
0x38, 0xa7, 0xdb, 0x13,
],
ovk: [
0x98, 0xd6, 0xa4, 0xbf, 0x68, 0x01, 0xd8, 0xba, 0x0d, 0x0b, 0x67, 0xea, 0x7b, 0x80,
0x52, 0x07, 0xab, 0xc0, 0x34, 0x8f, 0xc5, 0x62, 0x00, 0x5a, 0x59, 0xa2, 0x7a, 0x8a,
0x46, 0xfa, 0x6a, 0xdd,
],
dk: [
0xd0, 0xba, 0xef, 0x60, 0x12, 0xd3, 0x08, 0xef, 0xbb, 0x76, 0x9a, 0x99, 0xcc, 0xa2,
0x92, 0x8c, 0xed, 0xe8, 0xdb, 0x27, 0x76, 0x45, 0xa7, 0x77, 0xea, 0xf1, 0x72, 0x2c,
0xd0, 0x84, 0x50, 0xb3,
],
default_d: [
0xcc, 0x7c, 0xe7, 0x34, 0xb0, 0x75, 0xa0, 0x1b, 0x92, 0xaa, 0xca,
],
default_pk_d: [
0x3d, 0xa5, 0x27, 0x3a, 0x56, 0x67, 0xc7, 0x66, 0xb8, 0x23, 0x12, 0x06, 0x18, 0x0f,
0x15, 0x8a, 0xc0, 0x2a, 0xf3, 0xf0, 0x6e, 0xcc, 0xa6, 0xec, 0x7c, 0x38, 0xc7, 0x5d,
0x33, 0x60, 0x03, 0x20,
],
note_v: 5706402952489856202,
note_rho: [
0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, 0x49, 0xe9,
0x50, 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, 0x1f, 0xe3, 0x89, 0x5b, 0x8c,
0xc3, 0xd1, 0x4d, 0x2c,
],
note_rseed: [
0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a, 0x69, 0xf5, 0x33, 0x57,
0xd7, 0x76, 0x7f, 0x4f, 0x5c, 0xcb, 0xdb, 0xc5, 0x96, 0x63, 0x12, 0x77, 0xf8, 0xfe,
0xcd, 0x08, 0xcb, 0x05,
],
note_cmx: [
0x63, 0xce, 0xe3, 0x7e, 0x3c, 0x7b, 0x4e, 0x6c, 0xc9, 0x39, 0xa2, 0xe6, 0x3a, 0xda,
0x74, 0xf8, 0x5e, 0xa4, 0x8b, 0xa0, 0x7a, 0x4f, 0x92, 0xcc, 0xbd, 0x34, 0xfa, 0xa4,
0x2d, 0xfd, 0x49, 0x16,
],
note_nf: [
0x4c, 0x99, 0xbf, 0xa8, 0xc2, 0x0d, 0xba, 0x59, 0xbb, 0x73, 0x47, 0xda, 0x16, 0xc4,
0x3b, 0x73, 0xc8, 0x87, 0x94, 0xc9, 0xeb, 0xcd, 0x0d, 0xd2, 0xb2, 0x5e, 0xe7, 0xbb,
0x83, 0x6f, 0x95, 0x20,
],
},
TestVector {
sk: [
0x6b, 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2, 0x44, 0xfc, 0x71, 0x62,
0x69, 0xb9, 0x26, 0xd6, 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff,
0x9e, 0x68, 0x62, 0x5a,
],
ask: [
0x75, 0x7d, 0x15, 0x8d, 0x07, 0x35, 0x6b, 0x3b, 0xc2, 0xc9, 0xe5, 0x1c, 0x55, 0x8a,
0x9b, 0x31, 0x6b, 0xdd, 0xbc, 0x36, 0x0b, 0x8b, 0xeb, 0x6e, 0x2a, 0xe3, 0xb0, 0x61,
0x8f, 0x06, 0x2d, 0x2e,
],
ak: [
0x44, 0x9a, 0x90, 0xd2, 0xe8, 0xd1, 0xa0, 0x37, 0x64, 0x2a, 0x97, 0x09, 0x6c, 0x91,
0x65, 0x43, 0x46, 0x2a, 0x13, 0x7f, 0xfe, 0xa3, 0x7b, 0xaf, 0x41, 0xef, 0x28, 0x6b,
0xb7, 0x32, 0xbe, 0x2c,
],
nk: [
0xfd, 0x31, 0x64, 0xc6, 0x32, 0xbe, 0xc9, 0x4c, 0xe9, 0xfb, 0x2f, 0x30, 0x22, 0x63,
0xb8, 0x84, 0xab, 0xb9, 0xc1, 0x0e, 0x55, 0xe4, 0x48, 0x64, 0x7f, 0x67, 0x98, 0x49,
0x5c, 0x9d, 0x08, 0x3f,
],
rivk: [
0xc0, 0xb3, 0x6b, 0x56, 0x07, 0x0f, 0xff, 0x2f, 0xdf, 0x38, 0xeb, 0xa1, 0x1a, 0x74,
0x24, 0x95, 0x71, 0x95, 0x01, 0x4c, 0xba, 0x43, 0xa5, 0x6b, 0xd1, 0xb1, 0x65, 0x8e,
0x66, 0xa3, 0x9d, 0x00,
],
ivk: [
0x97, 0x6a, 0x87, 0x88, 0x19, 0x1b, 0x87, 0xe4, 0xc1, 0x3f, 0x2c, 0x6d, 0x23, 0xb4,
0xf3, 0x59, 0x5e, 0x02, 0x28, 0xe2, 0x45, 0xe9, 0x6e, 0xef, 0x1d, 0x24, 0xb2, 0x93,
0x29, 0x6a, 0x19, 0x1c,
],
ovk: [
0x1e, 0xd0, 0xed, 0xa5, 0xa4, 0x08, 0x61, 0x31, 0x26, 0x1a, 0x2e, 0xd4, 0x42, 0x92,
0x61, 0xe4, 0x27, 0x6a, 0x26, 0xd4, 0x28, 0x59, 0xfa, 0xbd, 0xa3, 0x1a, 0xa9, 0x67,
0x09, 0x87, 0x43, 0x71,
],
dk: [
0x5e, 0x5b, 0x60, 0xc0, 0x5b, 0x53, 0xd0, 0xbc, 0xd2, 0xda, 0x46, 0xa1, 0x31, 0x29,
0x12, 0x51, 0x5c, 0xc7, 0xcf, 0x2d, 0x97, 0x4c, 0x11, 0x7c, 0x8d, 0xde, 0xa9, 0xfa,
0xb6, 0x20, 0xc6, 0x68,
],
default_d: [
0x99, 0xaf, 0x6b, 0xf3, 0xf4, 0x75, 0xbd, 0xe8, 0x89, 0xaa, 0xca,
],
default_pk_d: [
0xac, 0xdc, 0xd3, 0x48, 0xca, 0x45, 0xee, 0x58, 0x32, 0x78, 0x30, 0x38, 0x46, 0xca,
0x07, 0x84, 0x59, 0xd5, 0xbe, 0x5c, 0x5d, 0xcf, 0x34, 0x7e, 0x3b, 0x9a, 0x34, 0xcb,
0xa1, 0x24, 0xb4, 0xa3,
],
note_v: 2558469029534639129,
note_rho: [
0x72, 0x2d, 0xb0, 0x41, 0xa3, 0xef, 0x66, 0xfa, 0x48, 0x3a, 0xfd, 0x3c, 0x2e, 0x19,
0xe5, 0x94, 0x44, 0xa6, 0x4a, 0xdd, 0x6d, 0xf1, 0xd9, 0x63, 0xf5, 0xdd, 0x5b, 0x50,
0x10, 0xd3, 0xd0, 0x25,
],
note_rseed: [
0xf0, 0x28, 0x7c, 0x4c, 0xf1, 0x9c, 0x75, 0xf3, 0x3d, 0x51, 0xdd, 0xdd, 0xba, 0x5d,
0x65, 0x7b, 0x43, 0xee, 0x8d, 0xa6, 0x45, 0x44, 0x38, 0x14, 0xcc, 0x73, 0x29, 0xf3,
0xe9, 0xb4, 0xe5, 0x4c,
],
note_cmx: [
0x1e, 0x61, 0x9e, 0x46, 0xbb, 0x62, 0xb6, 0x1d, 0x4e, 0x1c, 0xf3, 0x62, 0x2e, 0xa7,
0x0a, 0x90, 0x8d, 0xe7, 0xf0, 0x76, 0xec, 0xf8, 0x7f, 0x54, 0x1e, 0x0b, 0x7b, 0x48,
0xad, 0x4a, 0x26, 0x01,
],
note_nf: [
0x3b, 0x94, 0x8d, 0xb2, 0x16, 0x08, 0xe9, 0xac, 0xb2, 0x2a, 0x54, 0x17, 0xb9, 0x8c,
0x0d, 0xed, 0xd5, 0x27, 0xa9, 0x64, 0x87, 0x81, 0x4e, 0x64, 0x20, 0xcb, 0xff, 0x6e,
0x4e, 0xee, 0x4e, 0x31,
],
},
TestVector {
sk: [
0x23, 0x6c, 0x29, 0xaf, 0x39, 0x23, 0x10, 0x17, 0x56, 0xd9, 0xfa, 0x4b, 0xd0, 0xf7,
0xd2, 0xdd, 0xaa, 0xcb, 0x6b, 0x0f, 0x86, 0xa2, 0x65, 0x8e, 0x0a, 0x07, 0xa0, 0x5a,
0xc5, 0xb9, 0x50, 0x05,
],
ask: [
0xb4, 0xde, 0xd9, 0x0d, 0x62, 0x11, 0x7f, 0x18, 0xf3, 0xdd, 0x5f, 0xdb, 0x22, 0x23,
0x8a, 0x35, 0xca, 0x37, 0xc4, 0x0f, 0xee, 0xc8, 0x45, 0xce, 0x5f, 0xc2, 0x7f, 0xe8,
0xbc, 0xa5, 0xef, 0x0f,
],
ak: [
0x4e, 0xfd, 0x5a, 0x2e, 0xf1, 0xff, 0xa9, 0x9a, 0x0f, 0xf6, 0x2b, 0x76, 0x7d, 0x44,
0xb3, 0x65, 0x1f, 0xfa, 0x1c, 0x69, 0x69, 0x15, 0xac, 0x00, 0xa2, 0x5e, 0xa3, 0xac,
0x7d, 0xff, 0x99, 0x01,
],
nk: [
0x02, 0xab, 0x99, 0x5c, 0xe9, 0x8f, 0x63, 0x02, 0x5f, 0xb6, 0x24, 0x28, 0xa0, 0xfb,
0xf5, 0x2f, 0x25, 0x22, 0xe6, 0xa2, 0x72, 0x61, 0x07, 0x8a, 0x9f, 0x4d, 0x6a, 0x36,
0xa1, 0xc0, 0x5d, 0x39,
],
rivk: [
0xd9, 0x84, 0x0d, 0x0b, 0xd8, 0x95, 0x20, 0xab, 0xbc, 0xa7, 0xf1, 0x0b, 0xe6, 0xeb,
0xa3, 0x66, 0xf8, 0x6e, 0xc3, 0xb7, 0x8d, 0xbd, 0xf1, 0xeb, 0xfe, 0x20, 0xd9, 0x95,
0x12, 0xaf, 0x15, 0x15,
],
ivk: [
0x58, 0xf5, 0xbb, 0x5c, 0x32, 0x31, 0x15, 0x25, 0x29, 0x42, 0x3b, 0x67, 0xfa, 0x43,
0x28, 0x79, 0x11, 0x26, 0x35, 0xcd, 0xa0, 0xda, 0x2e, 0xc2, 0x41, 0x9c, 0x6f, 0xe9,
0x1e, 0xa4, 0x8d, 0x24,
],
ovk: [
0x78, 0xf5, 0xd3, 0x48, 0x67, 0x2e, 0x8d, 0x20, 0x9c, 0x41, 0xb7, 0x83, 0xf8, 0xca,
0x14, 0xa7, 0x7b, 0x3e, 0xa3, 0xe6, 0x00, 0x4c, 0xa4, 0xe0, 0xc2, 0x5a, 0xa4, 0x45,
0x63, 0x98, 0x1d, 0xcb,
],
dk: [
0x5d, 0x7f, 0xe3, 0x96, 0xbb, 0xfd, 0x22, 0x67, 0xac, 0xa7, 0x11, 0xab, 0x5b, 0x3e,
0x1f, 0x02, 0x4f, 0x49, 0x11, 0xf3, 0xa1, 0x81, 0x73, 0x2f, 0x13, 0x22, 0xa1, 0x59,
0x2f, 0x9e, 0x0e, 0xbe,
],
default_d: [
0x2f, 0xbe, 0x4b, 0x4b, 0x1e, 0xdf, 0xf3, 0x31, 0x23, 0xce, 0x65,
],
default_pk_d: [
0xeb, 0x2c, 0x6f, 0xee, 0x34, 0x1e, 0xad, 0xe0, 0x7d, 0x74, 0x87, 0x99, 0x7a, 0xa7,
0x23, 0x69, 0x7d, 0x05, 0xe6, 0x29, 0x60, 0xdf, 0x37, 0x9c, 0x9e, 0x4a, 0x8d, 0x47,
0x6d, 0xfa, 0xc5, 0xbf,
],
note_v: 15425828902564319772,
note_rho: [
0x73, 0x6c, 0x23, 0x35, 0x7c, 0x85, 0xf4, 0x57, 0x91, 0xe1, 0x70, 0x80, 0x29, 0xd9,
0x82, 0x4d, 0x90, 0x70, 0x46, 0x07, 0xf3, 0x87, 0xa0, 0x3e, 0x49, 0xbf, 0x98, 0x36,
0x57, 0x44, 0x31, 0x34,
],
note_rseed: [
0x5a, 0x78, 0x77, 0xef, 0xaa, 0x8a, 0x08, 0xe7, 0x30, 0x81, 0xef, 0x8d, 0x62, 0xcb,
0x78, 0x0a, 0xb6, 0x88, 0x3a, 0x50, 0xa0, 0xd4, 0x70, 0x19, 0x0d, 0xfb, 0xa1, 0x0a,
0x85, 0x7f, 0x82, 0x84,
],
note_cmx: [
0xc8, 0x52, 0x8f, 0x72, 0x2c, 0xd3, 0xe4, 0x7d, 0xc9, 0x9e, 0x1e, 0x38, 0x80, 0x56,
0x37, 0x08, 0x15, 0xa9, 0xd0, 0x37, 0x97, 0x3d, 0x85, 0xca, 0xc7, 0xea, 0x38, 0xb5,
0xa7, 0x16, 0xfa, 0x3b,
],
note_nf: [
0xac, 0xc2, 0xed, 0x2c, 0x7e, 0x3b, 0x19, 0x7e, 0x5c, 0xdb, 0x4a, 0x57, 0x63, 0x57,
0xd5, 0xf1, 0x35, 0x39, 0x16, 0x26, 0xc7, 0xa8, 0x25, 0xd1, 0x0a, 0xa2, 0x60, 0xae,
0x0b, 0x95, 0x81, 0x28,
],
},
TestVector {
sk: [
0x2d, 0x38, 0x25, 0xb3, 0xd6, 0xda, 0x05, 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, 0xc0,
0xb7, 0x16, 0xc4, 0x8f, 0xbd, 0x46, 0x7f, 0x75, 0xb7, 0x80, 0x14, 0x9a, 0xe8, 0x80,
0x8f, 0x4e, 0x68, 0xf5,
],
ask: [
0x2d, 0x6e, 0x97, 0x3e, 0x17, 0x54, 0xd4, 0x17, 0x87, 0x93, 0x4c, 0x34, 0x55, 0x8c,
0xfe, 0x99, 0x38, 0x44, 0x19, 0x99, 0x72, 0xd9, 0xa6, 0x34, 0x8b, 0x7a, 0x3d, 0xad,
0xfc, 0xb6, 0x77, 0x2a,
],
ak: [
0x76, 0x21, 0x59, 0xa4, 0x14, 0xf5, 0x74, 0xb5, 0x39, 0x75, 0x0f, 0x22, 0xc8, 0x86,
0x3b, 0x02, 0xd2, 0x5c, 0xc1, 0x0c, 0x90, 0x71, 0xfc, 0x02, 0x19, 0xe9, 0x7f, 0x93,
0x92, 0xd0, 0x67, 0x0c,
],
nk: [
0x25, 0x91, 0xed, 0xf7, 0xef, 0x4c, 0xf2, 0x18, 0x4c, 0x34, 0xbe, 0x93, 0xfc, 0xf6,
0x12, 0x91, 0x50, 0x42, 0xf1, 0x5a, 0xb5, 0x08, 0x4b, 0x14, 0xe1, 0x66, 0x79, 0x5b,
0x09, 0xce, 0xa1, 0x33,
],
rivk: [
0x75, 0x8f, 0xb2, 0x50, 0xdd, 0x29, 0x50, 0xe5, 0xd2, 0xb2, 0xee, 0xd7, 0xff, 0xcf,
0x94, 0xae, 0x67, 0xcd, 0xe1, 0x25, 0xb9, 0x5b, 0x47, 0x9e, 0x23, 0x77, 0x81, 0x3a,
0x85, 0xa0, 0x3d, 0x2f,
],
ivk: [
0x6e, 0xa4, 0x36, 0x3c, 0xb2, 0xdf, 0x62, 0xb1, 0x0d, 0xa1, 0x30, 0x8a, 0x0b, 0x96,
0x79, 0xbd, 0x0f, 0x74, 0x95, 0xff, 0xe7, 0xd4, 0xe2, 0x61, 0x8f, 0x54, 0xdf, 0x9b,
0x67, 0x0c, 0x33, 0x16,
],
ovk: [
0xa6, 0x3c, 0xbc, 0xd3, 0x1b, 0xa1, 0x36, 0xd8, 0x3b, 0x8f, 0x1e, 0x88, 0xef, 0xb6,
0x00, 0x55, 0xef, 0x6f, 0x98, 0x25, 0x2d, 0xdb, 0xd7, 0x5f, 0x62, 0x5f, 0x44, 0xdc,
0xb6, 0x63, 0x2c, 0x72,
],
dk: [
0x02, 0xf0, 0x74, 0x08, 0xf3, 0x3e, 0x87, 0x12, 0xe4, 0xc9, 0xec, 0x42, 0xde, 0x56,
0x04, 0x20, 0x01, 0x09, 0x86, 0x17, 0x24, 0xd3, 0x3e, 0xb6, 0x36, 0x8b, 0x70, 0xf6,
0x5e, 0x0a, 0x16, 0x21,
],
default_d: [
0x08, 0xdf, 0x1d, 0x4b, 0x45, 0xc6, 0x73, 0xa4, 0x59, 0xff, 0x58,
],
default_pk_d: [
0x26, 0x8c, 0xc2, 0x4b, 0x38, 0xa6, 0x28, 0x80, 0xb6, 0xee, 0x3c, 0xbc, 0xb8, 0x5a,
0x71, 0x2f, 0xa6, 0x86, 0xcf, 0xfc, 0xa6, 0xdb, 0x2f, 0xee, 0xc5, 0xf3, 0xc3, 0x56,
0x6f, 0x84, 0x21, 0x8f,
],
note_v: 12606128263924155660,
note_rho: [
0x12, 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, 0x40, 0x59, 0xf3, 0x96,
0xbf, 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, 0xa9, 0x44, 0xf7, 0x2d,
0x43, 0x6a, 0x10, 0x2f,
],
note_rseed: [
0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, 0xb0, 0x86, 0xfe, 0x9d, 0x2e, 0x71, 0x62, 0x47,
0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, 0xec, 0x95, 0x12, 0xbf, 0xb3, 0xf3, 0x83, 0x27,
0x29, 0x6e, 0xfa, 0xa7,
],
note_cmx: [
0x6a, 0x11, 0x95, 0xaa, 0x05, 0x36, 0xf6, 0x0e, 0xcf, 0xae, 0xcb, 0xdf, 0x53, 0x74,
0xe4, 0x94, 0xea, 0x07, 0x2a, 0x2b, 0x86, 0x7b, 0x5f, 0x69, 0x43, 0x40, 0xc9, 0x6f,
0xc3, 0x70, 0xa9, 0x10,
],
note_nf: [
0xb0, 0xf1, 0x60, 0x2a, 0x2b, 0x1a, 0xf2, 0xfc, 0x55, 0xf1, 0x59, 0x50, 0xa6, 0x83,
0x83, 0x85, 0xe5, 0xe3, 0x9f, 0xec, 0xfd, 0x05, 0xcc, 0xec, 0x79, 0x9b, 0x75, 0xc6,
0x5c, 0x8d, 0xa2, 0x35,
],
},
TestVector {
sk: [
0x43, 0x28, 0xb1, 0x18, 0xc2, 0x74, 0x02, 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4,
0xbb, 0xc6, 0x8e, 0x37, 0xc0, 0xaa, 0x7d, 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, 0x3b,
0x84, 0x1e, 0x75, 0x17,
],
ask: [
0x28, 0xdc, 0x45, 0xf1, 0x15, 0x44, 0x42, 0x5c, 0x1b, 0xef, 0x86, 0x61, 0xda, 0x11,
0x15, 0x5f, 0xdb, 0xb7, 0xe3, 0xbc, 0xfc, 0x0f, 0x0d, 0x49, 0xe6, 0xf1, 0x31, 0xe7,
0xc0, 0x9d, 0x35, 0x2f,
],
ak: [
0x0d, 0x21, 0x1a, 0x90, 0x60, 0xfb, 0xaa, 0x66, 0x4e, 0x41, 0xa7, 0x34, 0xad, 0x1d,
0x8d, 0x4b, 0x02, 0x5f, 0x8c, 0xc1, 0x60, 0xe1, 0xf4, 0xe9, 0x5f, 0x0a, 0x85, 0x3e,
0xbc, 0x41, 0x6a, 0x2b,
],
nk: [
0x3e, 0x88, 0xf2, 0x07, 0x1f, 0xd9, 0xa2, 0xbb, 0x26, 0xcd, 0xa2, 0xea, 0x85, 0x6a,
0xa0, 0xfb, 0x3a, 0x80, 0xa8, 0x7d, 0x2f, 0xb6, 0x13, 0x6f, 0xab, 0x85, 0xe3, 0x6c,
0x5b, 0x38, 0xd8, 0x24,
],
rivk: [
0x2c, 0x37, 0x38, 0x82, 0xc4, 0x08, 0xcd, 0x5f, 0xd4, 0x82, 0xa0, 0xc9, 0x81, 0x6f,
0xc3, 0x22, 0x03, 0xa1, 0x0f, 0xbf, 0xce, 0x0e, 0x20, 0x0c, 0xcf, 0xd9, 0xee, 0x30,
0x7c, 0x5e, 0x12, 0x24,
],
ivk: [
0xbb, 0x9e, 0x20, 0xb2, 0x99, 0x1c, 0x99, 0x6d, 0xa2, 0x1e, 0x3e, 0xcd, 0x39, 0xfb,
0x7b, 0x3a, 0xa2, 0xba, 0xbc, 0x6b, 0xde, 0x18, 0x6f, 0x7d, 0xd8, 0xa8, 0x75, 0xd1,
0x0c, 0x51, 0xa4, 0x30,
],
ovk: [
0x93, 0x21, 0x83, 0x8a, 0x2d, 0xb7, 0xf1, 0x68, 0xf0, 0xce, 0x77, 0xc4, 0x5b, 0x21,
0x1f, 0xfb, 0xb9, 0xb3, 0x65, 0xe8, 0x5e, 0x67, 0x31, 0xd9, 0x09, 0x70, 0x05, 0x53,
0xde, 0x49, 0x2b, 0x28,
],
dk: [
0x3d, 0xf5, 0x83, 0x36, 0x1b, 0x33, 0x38, 0xbb, 0x68, 0x15, 0xf8, 0x58, 0x72, 0xe3,
0x9f, 0x04, 0xdf, 0x50, 0x08, 0x52, 0x48, 0x84, 0xaf, 0x0f, 0x8c, 0x55, 0x97, 0x16,
0xfc, 0xb1, 0x49, 0x58,
],
default_d: [
0x4c, 0x40, 0x64, 0xc4, 0x7a, 0x5c, 0xa6, 0xe7, 0x5d, 0x46, 0x44,
],
default_pk_d: [
0xf5, 0x17, 0x17, 0x4b, 0xe2, 0x58, 0x92, 0x32, 0x78, 0xcf, 0x45, 0x89, 0x08, 0xc0,
0x73, 0x56, 0x49, 0xf1, 0x89, 0x9d, 0xb9, 0x9c, 0x3b, 0xa9, 0x00, 0x3f, 0x4b, 0xa3,
0x0a, 0xb0, 0xd2, 0x10,
],
note_v: 625536973899669523,
note_rho: [
0x03, 0xfd, 0x69, 0x44, 0x2e, 0xb7, 0x68, 0x1e, 0xc2, 0xa0, 0x56, 0x00, 0x05, 0x4e,
0x92, 0xee, 0xd5, 0x55, 0x02, 0x8f, 0x21, 0xb6, 0xa1, 0x55, 0x26, 0x8a, 0x2d, 0xd6,
0x64, 0x0a, 0x69, 0x30,
],
note_rseed: [
0x1a, 0x52, 0xa3, 0x8d, 0x4d, 0x9f, 0x9f, 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71,
0x18, 0x14, 0x1c, 0xe4, 0xc9, 0xbe, 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81,
0xa1, 0x55, 0xfa, 0x3a,
],
note_cmx: [
0xf7, 0x0e, 0xbf, 0x0f, 0x5e, 0xe5, 0xda, 0x6c, 0x6c, 0xde, 0xff, 0x8f, 0xec, 0x2f,
0x8e, 0xed, 0x65, 0xc8, 0x8e, 0x67, 0x55, 0xda, 0xf1, 0x14, 0xd5, 0x54, 0xaf, 0x19,
0x67, 0xa7, 0xf4, 0x0a,
],
note_nf: [
0x95, 0x64, 0x97, 0x28, 0x46, 0x5e, 0x68, 0x2a, 0xc0, 0x57, 0xad, 0x87, 0x62, 0x94,
0xd7, 0x00, 0xc2, 0x7f, 0xeb, 0xa2, 0xf7, 0x50, 0x92, 0x2f, 0x95, 0x51, 0x85, 0x70,
0x62, 0x61, 0xc3, 0x0c,
],
},
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,386 +0,0 @@
//! Types related to Orchard note commitment trees and anchors.
use crate::{
constants::{
sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
util::gen_const_array_with_default,
MERKLE_DEPTH_ORCHARD,
},
note::commitment::ExtractedNoteCommitment,
primitives::sinsemilla::HashDomain,
};
use incrementalmerkletree::{Altitude, Hashable};
use pasta_curves::pallas;
use ff::{Field, PrimeField, PrimeFieldBits};
use lazy_static::lazy_static;
use rand::RngCore;
use serde::de::{Deserializer, Error};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use std::iter;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
// The uncommitted leaf is defined as pallas::Base(2).
// <https://zips.z.cash/protocol/protocol.pdf#thmuncommittedorchard>
lazy_static! {
static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2);
pub(crate) static ref EMPTY_ROOTS: Vec<MerkleHashOrchard> = {
iter::empty()
.chain(Some(MerkleHashOrchard::empty_leaf()))
.chain(
(0..MERKLE_DEPTH_ORCHARD).scan(MerkleHashOrchard::empty_leaf(), |state, l| {
let l = l as u8;
*state = MerkleHashOrchard::combine(l.into(), state, state);
Some(*state)
}),
)
.collect()
};
}
/// The root of an Orchard commitment tree.
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub struct Anchor(pallas::Base);
impl From<pallas::Base> for Anchor {
fn from(anchor_field: pallas::Base) -> Anchor {
Anchor(anchor_field)
}
}
impl From<MerkleHashOrchard> for Anchor {
fn from(anchor: MerkleHashOrchard) -> Anchor {
Anchor(anchor.0)
}
}
impl Anchor {
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
}
impl Anchor {
/// Parses an Orchard anchor from a byte encoding.
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Anchor> {
pallas::Base::from_repr(bytes).map(Anchor)
}
/// Returns the byte encoding of this anchor.
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_repr()
}
}
/// The Merkle path from a leaf of the note commitment tree
/// to its anchor.
#[derive(Debug)]
pub struct MerklePath {
position: u32,
auth_path: [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD],
}
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
impl From<(incrementalmerkletree::Position, Vec<MerkleHashOrchard>)> for MerklePath {
fn from(path: (incrementalmerkletree::Position, Vec<MerkleHashOrchard>)) -> Self {
use std::convert::TryInto;
let position: u64 = path.0.into();
Self {
position: position as u32,
auth_path: path.1.try_into().unwrap(),
}
}
}
impl MerklePath {
/// Generates a dummy Merkle path for use in dummy spent notes.
pub(crate) fn dummy(mut rng: &mut impl RngCore) -> Self {
MerklePath {
position: rng.next_u32(),
auth_path: gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |_| {
MerkleHashOrchard(pallas::Base::random(&mut rng))
}),
}
}
/// Instantiates a new Merkle path given a leaf position and authentication path.
pub(crate) fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self {
Self {
position,
auth_path: gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |i| {
MerkleHashOrchard(auth_path[i])
}),
}
}
/// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
/// The layer with 2^n nodes is called "layer n":
/// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
/// - the root is at layer 0.
/// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
/// - when hashing two leaves, we produce a node on the layer above the leaves, i.e.
/// layer = 31, l = 0
/// - when hashing to the final root, we produce the anchor with layer = 0, l = 31.
pub fn root(&self, cmx: ExtractedNoteCommitment) -> Anchor {
self.auth_path
.iter()
.enumerate()
.fold(MerkleHashOrchard::from_cmx(&cmx), |node, (l, sibling)| {
let l = l as u8;
if self.position & (1 << l) == 0 {
MerkleHashOrchard::combine(l.into(), &node, sibling)
} else {
MerkleHashOrchard::combine(l.into(), sibling, &node)
}
})
.into()
}
/// Returns the position of the leaf using this Merkle path.
pub(crate) fn position(&self) -> u32 {
self.position
}
/// Returns the authentication path.
pub(crate) fn auth_path(&self) -> [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD] {
self.auth_path
}
}
/// A newtype wrapper for leaves and internal nodes in the Orchard
/// incremental note commitment tree.
#[derive(Copy, Clone, Debug)]
pub struct MerkleHashOrchard(pallas::Base);
impl MerkleHashOrchard {
/// Creates an incremental tree leaf digest from the specified
/// Orchard extracted note commitment.
pub fn from_cmx(value: &ExtractedNoteCommitment) -> Self {
MerkleHashOrchard(value.inner())
}
/// Only used in the circuit.
pub(crate) fn inner(&self) -> pallas::Base {
self.0
}
/// Convert this digest to its canonical byte representation.
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_repr()
}
/// Parses a incremental tree leaf digest from the bytes of
/// a note commitment.
///
/// Returns the empty `CtOption` if the provided bytes represent
/// a non-canonical encoding.
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_repr(*bytes).map(MerkleHashOrchard)
}
}
/// This instance should only be used for hash table key comparisons.
impl std::cmp::PartialEq for MerkleHashOrchard {
fn eq(&self, other: &Self) -> bool {
self.0.ct_eq(&other.0).into()
}
}
/// This instance should only be used for hash table key comparisons.
impl std::cmp::Eq for MerkleHashOrchard {}
/// This instance should only be used for hash table key hashing.
impl std::hash::Hash for MerkleHashOrchard {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
<Option<pallas::Base>>::from(self.0)
.map(|b| b.to_repr())
.hash(state)
}
}
impl ConditionallySelectable for MerkleHashOrchard {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
MerkleHashOrchard(pallas::Base::conditional_select(&a.0, &b.0, choice))
}
}
impl Hashable for MerkleHashOrchard {
fn empty_leaf() -> Self {
MerkleHashOrchard(*UNCOMMITTED_ORCHARD)
}
/// Implements `MerkleCRH^Orchard` as defined in
/// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
///
/// The layer with 2^n nodes is called "layer n":
/// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
/// - the root is at layer 0.
/// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
/// - when hashing two leaves, we produce a node on the layer above the leaves, i.e.
/// layer = 31, l = 0
/// - when hashing to the final root, we produce the anchor with layer = 0, l = 31.
fn combine(altitude: Altitude, left: &Self, right: &Self) -> Self {
// MerkleCRH Sinsemilla hash domain.
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
MerkleHashOrchard(
domain
.hash(
iter::empty()
.chain(i2lebsp_k(altitude.into()).iter().copied())
.chain(left.0.to_le_bits().iter().by_val().take(L_ORCHARD_MERKLE))
.chain(right.0.to_le_bits().iter().by_val().take(L_ORCHARD_MERKLE)),
)
.unwrap_or(pallas::Base::zero()),
)
}
fn empty_root(altitude: Altitude) -> Self {
EMPTY_ROOTS[<usize>::from(altitude)]
}
}
impl Serialize for MerkleHashOrchard {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_bytes().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for MerkleHashOrchard {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let parsed = <[u8; 32]>::deserialize(deserializer)?;
<Option<_>>::from(Self::from_bytes(&parsed)).ok_or_else(|| {
Error::custom(
"Attempted to deserialize a non-canonical representation of a Pallas base field element.",
)
})
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
#[cfg(test)]
use incrementalmerkletree::{
bridgetree::{BridgeTree, Frontier as BridgeFrontier},
Altitude, Frontier, Tree,
};
#[cfg(test)]
use crate::tree::{MerkleHashOrchard, EMPTY_ROOTS};
#[cfg(test)]
use group::ff::PrimeField;
#[cfg(test)]
use pasta_curves::pallas;
#[cfg(test)]
use std::convert::TryInto;
#[test]
fn test_vectors() {
let tv_empty_roots = crate::test_vectors::commitment_tree::test_vectors().empty_roots;
for (height, root) in EMPTY_ROOTS.iter().enumerate() {
assert_eq!(tv_empty_roots[height], root.to_bytes());
}
let mut tree = BridgeTree::<MerkleHashOrchard, 4>::new(100);
for (i, tv) in crate::test_vectors::merkle_path::test_vectors()
.into_iter()
.enumerate()
{
let cmx = MerkleHashOrchard::from_bytes(&tv.leaves[i]).unwrap();
tree.append(&cmx);
tree.witness();
assert_eq!(tree.root().0, pallas::Base::from_repr(tv.root).unwrap());
// Check paths for all leaves up to this point. The test vectors include paths
// for not-yet-appended leaves (using UNCOMMITTED_ORCHARD as the leaf value),
// but BridgeTree doesn't encode these.
for j in 0..=i {
let leaf = MerkleHashOrchard::from_bytes(&tv.leaves[j]).unwrap();
assert_eq!(
tree.authentication_path(&leaf),
Some((
j.try_into().unwrap(),
tv.paths[j]
.iter()
.map(|v| MerkleHashOrchard::from_bytes(v).unwrap())
.collect()
))
);
}
}
}
#[test]
fn empty_roots_incremental() {
use incrementalmerkletree::Hashable;
let tv_empty_roots = crate::test_vectors::commitment_tree::test_vectors().empty_roots;
for (altitude, tv_root) in tv_empty_roots.iter().enumerate() {
assert_eq!(
MerkleHashOrchard::empty_root(Altitude::from(altitude as u8))
.0
.to_repr(),
*tv_root,
"Empty root mismatch at altitude {}",
altitude
);
}
}
#[test]
fn anchor_incremental() {
// These commitment values are derived from the bundle data that was generated for
// testing commitment tree construction inside of zcashd here.
// https://github.com/zcash/zcash/blob/ecec1f9769a5e37eb3f7fd89a4fcfb35bc28eed7/src/test/data/merkle_roots_orchard.h
let commitments = [
[
0x68, 0x13, 0x5c, 0xf4, 0x99, 0x33, 0x22, 0x90, 0x99, 0xa4, 0x4e, 0xc9, 0x9a, 0x75,
0xe1, 0xe1, 0xcb, 0x46, 0x40, 0xf9, 0xb5, 0xbd, 0xec, 0x6b, 0x32, 0x23, 0x85, 0x6f,
0xea, 0x16, 0x39, 0x0a,
],
[
0x78, 0x31, 0x50, 0x08, 0xfb, 0x29, 0x98, 0xb4, 0x30, 0xa5, 0x73, 0x1d, 0x67, 0x26,
0x20, 0x7d, 0xc0, 0xf0, 0xec, 0x81, 0xea, 0x64, 0xaf, 0x5c, 0xf6, 0x12, 0x95, 0x69,
0x01, 0xe7, 0x2f, 0x0e,
],
[
0xee, 0x94, 0x88, 0x05, 0x3a, 0x30, 0xc5, 0x96, 0xb4, 0x30, 0x14, 0x10, 0x5d, 0x34,
0x77, 0xe6, 0xf5, 0x78, 0xc8, 0x92, 0x40, 0xd1, 0xd1, 0xee, 0x17, 0x43, 0xb7, 0x7b,
0xb6, 0xad, 0xc4, 0x0a,
],
[
0x9d, 0xdc, 0xe7, 0xf0, 0x65, 0x01, 0xf3, 0x63, 0x76, 0x8c, 0x5b, 0xca, 0x3f, 0x26,
0x46, 0x60, 0x83, 0x4d, 0x4d, 0xf4, 0x46, 0xd1, 0x3e, 0xfc, 0xd7, 0xc6, 0xf1, 0x7b,
0x16, 0x7a, 0xac, 0x1a,
],
[
0xbd, 0x86, 0x16, 0x81, 0x1c, 0x6f, 0x5f, 0x76, 0x9e, 0xa4, 0x53, 0x9b, 0xba, 0xff,
0x0f, 0x19, 0x8a, 0x6c, 0xdf, 0x3b, 0x28, 0x0d, 0xd4, 0x99, 0x26, 0x16, 0x3b, 0xd5,
0x3f, 0x53, 0xa1, 0x21,
],
];
// This value was produced by the Python test vector generation code implemented here:
// https://github.com/zcash-hackworks/zcash-test-vectors/blob/f4d756410c8f2456f5d84cedf6dac6eb8c068eed/orchard_merkle_tree.py
let anchor = [
0xc8, 0x75, 0xbe, 0x2d, 0x60, 0x87, 0x3f, 0x8b, 0xcd, 0xeb, 0x91, 0x28, 0x2e, 0x64,
0x2e, 0x0c, 0xc6, 0x5f, 0xf7, 0xd0, 0x64, 0x2d, 0x13, 0x7b, 0x28, 0xcf, 0x28, 0xcc,
0x9c, 0x52, 0x7f, 0x0e,
];
let mut frontier = BridgeFrontier::<MerkleHashOrchard, 32>::empty();
for commitment in commitments.iter() {
let cmx = MerkleHashOrchard(pallas::Base::from_repr(*commitment).unwrap());
frontier.append(&cmx);
}
assert_eq!(frontier.root().0, pallas::Base::from_repr(anchor).unwrap());
}
}

View File

@ -1,415 +0,0 @@
//! Monetary values within the Orchard shielded pool.
//!
//! Values are represented in three places within the Orchard protocol:
//! - [`NoteValue`], the value of an individual note. It is an unsigned 64-bit integer
//! (with maximum value [`MAX_NOTE_VALUE`]), and is serialized in a note plaintext.
//! - [`ValueSum`], the sum of note values within an Orchard [`Action`] or [`Bundle`].
//! It is a signed 64-bit integer (with range [`VALUE_SUM_RANGE`]).
//! - `valueBalanceOrchard`, which is a signed 63-bit integer. This is represented by a
//! user-defined type parameter on [`Bundle`], returned by [`Bundle::value_balance`].
//!
//! If your specific instantiation of the Orchard protocol requires a smaller bound on
//! valid note values (for example, Zcash's `MAX_MONEY` fits into a 51-bit integer), you
//! should enforce this in two ways:
//!
//! - Define your `valueBalanceOrchard` type to enforce your valid value range. This can
//! be checked in its `TryFrom<i64>` implementation.
//! - Define your own "amount" type for note values, and convert it to `NoteValue` prior
//! to calling [`Builder::add_recipient`].
//!
//! Inside the circuit, note values are constrained to be unsigned 64-bit integers.
//!
//! # Caution!
//!
//! An `i64` is _not_ a signed 64-bit integer! The [Rust documentation] calls `i64` the
//! 64-bit signed integer type, which is true in the sense that its encoding in memory
//! takes up 64 bits. Numerically, however, `i64` is a signed 63-bit integer.
//!
//! Fortunately, users of this crate should never need to construct [`ValueSum`] directly;
//! you should only need to interact with [`NoteValue`] (which can be safely constructed
//! from a `u64`) and `valueBalanceOrchard` (which can be represented as an `i64`).
//!
//! [`Action`]: crate::bundle::Action
//! [`Bundle`]: crate::bundle::Bundle
//! [`Bundle::value_balance`]: crate::bundle::Bundle::value_balance
//! [`Builder::add_recipient`]: crate::builder::Builder::add_recipient
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
use std::convert::{TryFrom, TryInto};
use std::fmt::{self, Debug};
use std::iter::Sum;
use std::ops::{Add, Sub};
use bitvec::{array::BitArray, order::Lsb0};
use ff::{Field, PrimeField};
use group::{Curve, Group, GroupEncoding};
use pasta_curves::{
arithmetic::{CurveAffine, CurveExt},
pallas,
};
use rand::RngCore;
use subtle::CtOption;
use crate::{
constants::fixed_bases::{
VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
},
primitives::redpallas::{self, Binding},
};
use std::ops::RangeInclusive;
/// Maximum note value.
pub const MAX_NOTE_VALUE: u64 = u64::MAX;
/// The valid range of the scalar multiplication used in ValueCommit^Orchard.
///
/// Defined in a note in [Zcash Protocol Spec § 4.17.4: Action Statement (Orchard)][actionstatement].
///
/// [actionstatement]: https://zips.z.cash/protocol/nu5.pdf#actionstatement
pub const VALUE_SUM_RANGE: RangeInclusive<i128> =
-(MAX_NOTE_VALUE as i128)..=MAX_NOTE_VALUE as i128;
/// A value operation overflowed.
#[derive(Debug)]
pub struct OverflowError;
impl fmt::Display for OverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Orchard value operation overflowed")
}
}
impl std::error::Error for OverflowError {}
/// The non-negative value of an individual Orchard note.
#[derive(Clone, Copy, Debug, Default)]
pub struct NoteValue(u64);
impl NoteValue {
pub(crate) fn zero() -> Self {
// Default for u64 is zero.
Default::default()
}
/// Returns the raw underlying value.
pub fn inner(&self) -> u64 {
self.0
}
/// Creates a note value from its raw numeric value.
///
/// This only enforces that the value is an unsigned 64-bit integer. Callers should
/// enforce any additional constraints on the value's valid range themselves.
pub fn from_raw(value: u64) -> Self {
NoteValue(value)
}
pub(crate) fn from_bytes(bytes: [u8; 8]) -> Self {
NoteValue(u64::from_le_bytes(bytes))
}
pub(crate) fn to_bytes(self) -> [u8; 8] {
self.0.to_le_bytes()
}
pub(crate) fn to_le_bits(self) -> BitArray<Lsb0, [u8; 8]> {
BitArray::<Lsb0, _>::new(self.0.to_le_bytes())
}
}
impl Sub for NoteValue {
type Output = Option<ValueSum>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self::Output {
let a = self.0 as i128;
let b = rhs.0 as i128;
a.checked_sub(b)
.filter(|v| VALUE_SUM_RANGE.contains(v))
.map(ValueSum)
}
}
/// A sum of Orchard note values.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ValueSum(i128);
impl ValueSum {
pub(crate) fn zero() -> Self {
// Default for i64 is zero.
Default::default()
}
}
impl Add for ValueSum {
type Output = Option<ValueSum>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self::Output {
self.0
.checked_add(rhs.0)
.filter(|v| VALUE_SUM_RANGE.contains(v))
.map(ValueSum)
}
}
impl<'a> Sum<&'a ValueSum> for Result<ValueSum, OverflowError> {
fn sum<I: Iterator<Item = &'a ValueSum>>(iter: I) -> Self {
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + *v).ok_or(OverflowError))
}
}
impl Sum<ValueSum> for Result<ValueSum, OverflowError> {
fn sum<I: Iterator<Item = ValueSum>>(iter: I) -> Self {
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + v).ok_or(OverflowError))
}
}
impl TryFrom<ValueSum> for i64 {
type Error = OverflowError;
fn try_from(v: ValueSum) -> Result<i64, Self::Error> {
i64::try_from(v.0).map_err(|_| OverflowError)
}
}
/// The blinding factor for a [`ValueCommitment`].
#[derive(Clone, Debug)]
pub struct ValueCommitTrapdoor(pallas::Scalar);
impl ValueCommitTrapdoor {
pub(crate) fn inner(&self) -> pallas::Scalar {
self.0
}
}
impl Add<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
type Output = ValueCommitTrapdoor;
fn add(self, rhs: &Self) -> Self::Output {
ValueCommitTrapdoor(self.0 + rhs.0)
}
}
impl<'a> Sum<&'a ValueCommitTrapdoor> for ValueCommitTrapdoor {
fn sum<I: Iterator<Item = &'a ValueCommitTrapdoor>>(iter: I) -> Self {
iter.fold(ValueCommitTrapdoor::zero(), |acc, cv| acc + cv)
}
}
impl ValueCommitTrapdoor {
/// Generates a new value commitment trapdoor.
pub(crate) fn random(rng: impl RngCore) -> Self {
ValueCommitTrapdoor(pallas::Scalar::random(rng))
}
/// Returns the zero trapdoor, which provides no blinding.
pub(crate) fn zero() -> Self {
ValueCommitTrapdoor(pallas::Scalar::zero())
}
pub(crate) fn into_bsk(self) -> redpallas::SigningKey<Binding> {
// TODO: impl From<pallas::Scalar> for redpallas::SigningKey.
self.0.to_repr().try_into().unwrap()
}
}
/// A commitment to a [`ValueSum`].
#[derive(Clone, Debug)]
pub struct ValueCommitment(pallas::Point);
impl Add<&ValueCommitment> for ValueCommitment {
type Output = ValueCommitment;
fn add(self, rhs: &Self) -> Self::Output {
ValueCommitment(self.0 + rhs.0)
}
}
impl Sub for ValueCommitment {
type Output = ValueCommitment;
fn sub(self, rhs: Self) -> Self::Output {
ValueCommitment(self.0 - rhs.0)
}
}
impl Sum for ValueCommitment {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(ValueCommitment(pallas::Point::identity()), |acc, cv| {
acc + &cv
})
}
}
impl<'a> Sum<&'a ValueCommitment> for ValueCommitment {
fn sum<I: Iterator<Item = &'a ValueCommitment>>(iter: I) -> Self {
iter.fold(ValueCommitment(pallas::Point::identity()), |acc, cv| {
acc + cv
})
}
}
impl ValueCommitment {
/// $ValueCommit^Orchard$.
///
/// Defined in [Zcash Protocol Spec § 5.4.8.3: Homomorphic Pedersen commitments (Sapling and Orchard)][concretehomomorphiccommit].
///
/// [concretehomomorphiccommit]: https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit
#[allow(non_snake_case)]
pub(crate) fn derive(value: ValueSum, rcv: ValueCommitTrapdoor) -> Self {
let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let V = hasher(&VALUE_COMMITMENT_V_BYTES);
let R = hasher(&VALUE_COMMITMENT_R_BYTES);
let abs_value = u64::try_from(value.0.abs()).expect("value must be in valid range");
let value = if value.0.is_negative() {
-pallas::Scalar::from(abs_value)
} else {
pallas::Scalar::from(abs_value)
};
ValueCommitment(V * value + R * rcv.0)
}
pub(crate) fn into_bvk(self) -> redpallas::VerificationKey<Binding> {
// TODO: impl From<pallas::Point> for redpallas::VerificationKey.
self.0.to_bytes().try_into().unwrap()
}
/// Deserialize a value commitment from its byte representation
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<ValueCommitment> {
pallas::Point::from_bytes(bytes).map(ValueCommitment)
}
/// Serialize this value commitment to its canonical byte representation.
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
/// x-coordinate of this value commitment.
pub(crate) fn x(&self) -> pallas::Base {
if self.0 == pallas::Point::identity() {
pallas::Base::zero()
} else {
*self.0.to_affine().coordinates().unwrap().x()
}
}
/// y-coordinate of this value commitment.
pub(crate) fn y(&self) -> pallas::Base {
if self.0 == pallas::Point::identity() {
pallas::Base::zero()
} else {
*self.0.to_affine().coordinates().unwrap().y()
}
}
}
/// Generators for property testing.
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use pasta_curves::{arithmetic::FieldExt, pallas};
use proptest::prelude::*;
use super::{NoteValue, ValueCommitTrapdoor, ValueSum, MAX_NOTE_VALUE, VALUE_SUM_RANGE};
prop_compose! {
/// Generate an arbitrary Pallas scalar.
pub fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> pallas::Scalar {
// Instead of rejecting out-of-range bytes, let's reduce them.
let mut buf = [0; 64];
buf[..32].copy_from_slice(&bytes);
pallas::Scalar::from_bytes_wide(&buf)
}
}
prop_compose! {
/// Generate an arbitrary [`ValueSum`] in the range of valid Zcash values.
pub fn arb_value_sum()(value in VALUE_SUM_RANGE) -> ValueSum {
ValueSum(value as i128)
}
}
prop_compose! {
/// Generate an arbitrary [`ValueSum`] in the range of valid Zcash values.
pub fn arb_value_sum_bounded(bound: NoteValue)(value in -(bound.0 as i128)..=(bound.0 as i128)) -> ValueSum {
ValueSum(value as i128)
}
}
prop_compose! {
/// Generate an arbitrary ValueCommitTrapdoor
pub fn arb_trapdoor()(rcv in arb_scalar()) -> ValueCommitTrapdoor {
ValueCommitTrapdoor(rcv)
}
}
prop_compose! {
/// Generate an arbitrary value in the range of valid nonnegative Zcash amounts.
pub fn arb_note_value()(value in 0u64..MAX_NOTE_VALUE) -> NoteValue {
NoteValue(value)
}
}
prop_compose! {
/// Generate an arbitrary value in the range of valid positive Zcash amounts
/// less than a specified value.
pub fn arb_note_value_bounded(max: u64)(value in 0u64..max) -> NoteValue {
NoteValue(value)
}
}
prop_compose! {
/// Generate an arbitrary value in the range of valid positive Zcash amounts
/// less than a specified value.
pub fn arb_positive_note_value(max: u64)(value in 1u64..max) -> NoteValue {
NoteValue(value)
}
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::{
testing::{arb_note_value_bounded, arb_trapdoor, arb_value_sum_bounded},
OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum, MAX_NOTE_VALUE,
};
use crate::primitives::redpallas;
proptest! {
#[test]
fn bsk_consistent_with_bvk(
values in (1usize..10).prop_flat_map(|n_values|
arb_note_value_bounded(MAX_NOTE_VALUE / n_values as u64).prop_flat_map(move |bound|
prop::collection::vec((arb_value_sum_bounded(bound), arb_trapdoor()), n_values)
)
)
) {
let value_balance = values
.iter()
.map(|(value, _)| value)
.sum::<Result<ValueSum, OverflowError>>()
.expect("we generate values that won't overflow");
let bsk = values
.iter()
.map(|(_, rcv)| rcv)
.sum::<ValueCommitTrapdoor>()
.into_bsk();
let bvk = (values
.into_iter()
.map(|(value, rcv)| ValueCommitment::derive(value, rcv))
.sum::<ValueCommitment>()
- ValueCommitment::derive(value_balance, ValueCommitTrapdoor::zero()))
.into_bvk();
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
}
}
}

View File

@ -1,246 +0,0 @@
//! Key structures for Orchard.
use std::{
convert::{TryFrom, TryInto},
fmt,
};
use blake2b_simd::Params as Blake2bParams;
use subtle::{Choice, ConstantTimeEq};
use crate::{
keys::{FullViewingKey, SpendingKey},
spec::PrfExpand,
};
const ZIP32_ORCHARD_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Orchard";
const ZIP32_ORCHARD_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashOrchardFVFP";
/// Errors produced in derivation of extended spending keys
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
/// A seed resulted in an invalid spending key
InvalidSpendingKey,
/// A child index in a derivation path exceeded 2^31
InvalidChildIndex(u32),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Seed produced invalid spending key.")
}
}
//impl std::error::Error for Error {}
/// An Orchard full viewing key fingerprint
struct FvkFingerprint([u8; 32]);
impl From<&FullViewingKey> for FvkFingerprint {
fn from(fvk: &FullViewingKey) -> Self {
let mut h = Blake2bParams::new()
.hash_length(32)
.personal(ZIP32_ORCHARD_FVFP_PERSONALIZATION)
.to_state();
h.update(&fvk.to_bytes());
let mut fvfp = [0u8; 32];
fvfp.copy_from_slice(h.finalize().as_bytes());
FvkFingerprint(fvfp)
}
}
/// An Orchard full viewing key tag
#[derive(Clone, Copy, Debug, PartialEq)]
struct FvkTag([u8; 4]);
impl FvkFingerprint {
fn tag(&self) -> FvkTag {
let mut tag = [0u8; 4];
tag.copy_from_slice(&self.0[..4]);
FvkTag(tag)
}
}
impl FvkTag {
fn master() -> Self {
FvkTag([0u8; 4])
}
}
/// A hardened child index for a derived key.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ChildIndex(u32);
impl TryFrom<u32> for ChildIndex {
type Error = Error;
/// `index` must be less than 2^31
fn try_from(index: u32) -> Result<Self, Self::Error> {
if index < (1 << 31) {
Ok(Self(index + (1 << 31)))
} else {
Err(Error::InvalidChildIndex(32))
}
}
}
/// The chain code forming the second half of an Orchard extended key.
#[derive(Debug, Copy, Clone, PartialEq)]
struct ChainCode([u8; 32]);
/// An Orchard extended spending key.
///
/// Defined in [ZIP32: Orchard extended keys][orchardextendedkeys].
///
/// [orchardextendedkeys]: https://zips.z.cash/zip-0032#orchard-extended-keys
#[derive(Debug, Clone)]
pub(crate) struct ExtendedSpendingKey {
depth: u8,
parent_fvk_tag: FvkTag,
child_index: ChildIndex,
chain_code: ChainCode,
sk: SpendingKey,
}
impl ConstantTimeEq for ExtendedSpendingKey {
fn ct_eq(&self, rhs: &Self) -> Choice {
self.depth.ct_eq(&rhs.depth)
& self.parent_fvk_tag.0.ct_eq(&rhs.parent_fvk_tag.0)
& self.child_index.0.ct_eq(&rhs.child_index.0)
& self.chain_code.0.ct_eq(&rhs.chain_code.0)
& self.sk.ct_eq(&rhs.sk)
}
}
#[allow(non_snake_case)]
impl ExtendedSpendingKey {
/// Returns the spending key of the child key corresponding to
/// the path derived from the master key
///
/// # Panics
///
/// Panics if seed results in invalid spending key.
pub fn from_path(seed: &[u8], path: &[ChildIndex]) -> Result<Self, Error> {
let mut xsk = Self::master(seed)?;
for i in path {
xsk = xsk.derive_child(*i)?;
}
Ok(xsk)
}
/// Generates the master key of an Orchard extended spending key.
///
/// Defined in [ZIP32: Orchard master key generation][orchardmasterkey].
///
/// [orchardmasterkey]: https://zips.z.cash/zip-0032#orchard-master-key-generation
///
/// # Panics
///
/// Panics if the seed is shorter than 32 bytes or longer than 252 bytes.
fn master(seed: &[u8]) -> Result<Self, Error> {
assert!(seed.len() >= 32 && seed.len() <= 252);
// I := BLAKE2b-512("ZcashIP32Orchard", seed)
let I: [u8; 64] = {
let mut I = Blake2bParams::new()
.hash_length(64)
.personal(ZIP32_ORCHARD_PERSONALIZATION)
.to_state();
I.update(seed);
I.finalize().as_bytes().try_into().unwrap()
};
// I_L is used as the master spending key sk_m.
let sk_m = SpendingKey::from_bytes(I[..32].try_into().unwrap());
if sk_m.is_none().into() {
return Err(Error::InvalidSpendingKey);
}
let sk_m = sk_m.unwrap();
// I_R is used as the master chain code c_m.
let c_m = ChainCode(I[32..].try_into().unwrap());
// For the master extended spending key, depth is 0, parent_fvk_tag is 4 zero bytes, and i is 0.
Ok(Self {
depth: 0,
parent_fvk_tag: FvkTag([0; 4]),
child_index: ChildIndex(0),
chain_code: c_m,
sk: sk_m,
})
}
/// Derives a child key from a parent key at a given index.
///
/// Defined in [ZIP32: Orchard child key derivation][orchardchildkey].
///
/// [orchardchildkey]: https://zips.z.cash/zip-0032#orchard-child-key-derivation
///
/// Discards index if it results in an invalid sk
fn derive_child(&self, index: ChildIndex) -> Result<Self, Error> {
// I := PRF^Expand(c_par, [0x81] || sk_par || I2LEOSP(i))
let I: [u8; 64] = PrfExpand::OrchardZip32Child.with_ad_slices(
&self.chain_code.0,
&[self.sk.to_bytes(), &index.0.to_le_bytes()],
);
// I_L is used as the child spending key sk_i.
let sk_i = SpendingKey::from_bytes(I[..32].try_into().unwrap());
if sk_i.is_none().into() {
return Err(Error::InvalidSpendingKey);
}
let sk_i = sk_i.unwrap();
// I_R is used as the child chain code c_i.
let c_i = ChainCode(I[32..].try_into().unwrap());
let fvk: FullViewingKey = self.into();
Ok(Self {
depth: self.depth + 1,
parent_fvk_tag: FvkFingerprint::from(&fvk).tag(),
child_index: index,
chain_code: c_i,
sk: sk_i,
})
}
/// Returns sk of this ExtendedSpendingKey.
pub fn sk(&self) -> SpendingKey {
self.sk
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derive_child() {
let seed = [0; 32];
let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
let i_5 = 5;
let xsk_5 = xsk_m.derive_child(i_5.try_into().unwrap());
assert!(xsk_5.is_ok());
}
#[test]
fn path() {
let seed = [0; 32];
let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
let xsk_5h = xsk_m.derive_child(5.try_into().unwrap()).unwrap();
assert!(bool::from(
ExtendedSpendingKey::from_path(&seed, &[5.try_into().unwrap()])
.unwrap()
.ct_eq(&xsk_5h)
));
let xsk_5h_7 = xsk_5h.derive_child(7.try_into().unwrap()).unwrap();
assert!(bool::from(
ExtendedSpendingKey::from_path(&seed, &[5.try_into().unwrap(), 7.try_into().unwrap()])
.unwrap()
.ct_eq(&xsk_5h_7)
));
}
}