Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ orchard_note_encryption = "zcash_test_vectors.orchard.note_encryption:main"
orchard_poseidon = "zcash_test_vectors.orchard.poseidon:main"
orchard_poseidon_hash = "zcash_test_vectors.orchard.poseidon:hash_test_vectors"
orchard_sinsemilla = "zcash_test_vectors.orchard.sinsemilla:main"
orchard_asset_id="zcash_test_vectors.orchard.asset_id:main"
1 change: 1 addition & 0 deletions regenerate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ tv_scripts=(
bip_0032
f4jumble
f4jumble_long
orchard_asset_id
orchard_empty_roots
orchard_generators
orchard_group_hash
Expand Down
24 changes: 24 additions & 0 deletions test-vectors/json/orchard_asset_id.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions test-vectors/json/orchard_key_components.json

Large diffs are not rendered by default.

32 changes: 21 additions & 11 deletions test-vectors/json/orchard_note_encryption.json

Large diffs are not rendered by default.

232 changes: 232 additions & 0 deletions test-vectors/rust/orchard_asset_id.rs

Large diffs are not rendered by default.

1,505 changes: 659 additions & 846 deletions test-vectors/rust/orchard_key_components.rs

Large diffs are not rendered by default.

102 changes: 51 additions & 51 deletions test-vectors/rust/orchard_note_encryption.rs

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions test-vectors/zcash/orchard_asset_id.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions test-vectors/zcash/orchard_key_components.json

Large diffs are not rendered by default.

34 changes: 22 additions & 12 deletions test-vectors/zcash/orchard_note_encryption.json

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions zcash_test_vectors/orchard/asset_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
import sys;

assert sys.version_info[0] >= 3, "Python 3 required."

import random

from ..orchard.group_hash import group_hash
from ..output import render_args, render_tv, option


def native_asset():
return group_hash(b"z.cash:Orchard-cv", b"v")

def asset_id(key, description):
return group_hash(b"z.cash:Orchard-cv", key + description)

def get_random_unicode_bytes(length):
try:
get_char = unichr
except NameError:
get_char = chr

# Update this to include code point ranges to be sampled
include_ranges = [
( 0x0021, 0x0021 ),
( 0x0023, 0x0026 ),
( 0x0028, 0x007E ),
( 0x00A1, 0x00AC ),
( 0x00AE, 0x00FF ),
( 0x0100, 0x017F ),
( 0x0180, 0x024F ),
( 0x2C60, 0x2C7F ),
( 0x16A0, 0x16F0 ),
( 0x0370, 0x0377 ),
( 0x037A, 0x037E ),
( 0x0384, 0x038A ),
( 0x038C, 0x038C ),
]

alphabet = [
get_char(code_point) for current_range in include_ranges
for code_point in range(current_range[0], current_range[1] + 1)
]
description_bytes = ''.join(random.choice(alphabet) for i in range(length)).encode("UTF-8")[:length].decode('UTF-8', 'ignore').encode('UTF-8').ljust(length, b'Z')
return description_bytes

def main():
args = render_args()

from zcash_test_vectors.rand import Rand
from zcash_test_vectors.orchard.key_components import SpendingKey
from zcash_test_vectors.orchard.key_components import FullViewingKey
from random import Random

rng = Random(0xabad533d)

def randbytes(l):
ret = []
while len(ret) < l:
ret.append(rng.randrange(0, 256))
return bytes(ret)

rand = Rand(randbytes)

test_vectors = []
for i in range(0, 20):
sk = SpendingKey(rand.b(32))
fvk = FullViewingKey.from_spending_key(sk)

key_bytes = bytes(fvk.ivk())
description_bytes = get_random_unicode_bytes(512)
asset = asset_id(key_bytes, description_bytes)

test_vectors.append({
'key': key_bytes,
'description': description_bytes,
'asset_id': bytes(asset),
})

render_tv(
args,
'orchard_asset_id',
(
('key', '[u8; 32]'),
('description', '[u8; 512]'),
('asset_id', '[u8; 32]'),
),
test_vectors,
)


if __name__ == '__main__':
main()
34 changes: 22 additions & 12 deletions zcash_test_vectors/orchard/commitments.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#!/usr/bin/env python3
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
import sys;

from zcash_test_vectors.orchard.asset_id import asset_id, native_asset

assert sys.version_info[0] >= 3, "Python 3 required."

from .group_hash import group_hash
from .pallas import Fp, Scalar
from .pallas import Fp, Scalar, Point
from .sinsemilla import sinsemilla_hash_to_point
from ..utils import i2lebsp

Expand All @@ -15,8 +19,8 @@
def homomorphic_pedersen_commitment(rcv: Scalar, D, v: Scalar):
return group_hash(D, b"v") * v + group_hash(D, b"r") * rcv

def value_commit(rcv: Scalar, v: Scalar):
return homomorphic_pedersen_commitment(rcv, b"z.cash:Orchard-cv", v)
def value_commit(rcv: Scalar, v: Scalar, asset: Point):
return asset * v + group_hash(b"z.cash:Orchard-cv", b"r") * rcv

def rcv_trapdoor(rand):
return Scalar.random(rand)
Expand All @@ -32,9 +36,9 @@ def sinsemilla_short_commit(r: Scalar, D, M):
return sinsemilla_commit(r, D, M).extract()

# ZIP-226 (https://github.com/zcash/zips/pull/628)
def note_commit(rcm, g_d, pk_d, v, note_type, rho, psi):
if note_type:
return note_commit_zsa(rcm, g_d, pk_d, v, note_type, rho, psi)
def note_commit(rcm, g_d, pk_d, v, asset, rho, psi):
if asset:
return note_commit_zsa(rcm, g_d, pk_d, v, asset, rho, psi)
else:
return note_commit_orchard(rcm, g_d, pk_d, v, rho, psi)

Expand All @@ -46,11 +50,11 @@ def note_commit_orchard(rcm, g_d, pk_d, v, rho, psi):
g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s)
)

def note_commit_zsa(rcm, g_d, pk_d, v, note_type, rho, psi):
def note_commit_zsa(rcm, g_d, pk_d, v, asset, rho, psi):
return sinsemilla_commit(
rcm,
b"z.cash:ZSA-NoteCommit",
g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s) + note_type
g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s) + asset
)

def rcm_trapdoor(rand):
Expand All @@ -71,7 +75,7 @@ def rivk_trapdoor(rand):
def test_value_commit():
from random import Random
from ..rand import Rand
from .generators import VALUE_COMMITMENT_RANDOMNESS_BASE, VALUE_COMMITMENT_VALUE_BASE
from .generators import VALUE_COMMITMENT_RANDOMNESS_BASE

rng = Random(0xabad533d)
def randbytes(l):
Expand All @@ -82,9 +86,15 @@ def randbytes(l):
rand = Rand(randbytes)

rcv = rcv_trapdoor(rand)
v = Scalar(100000000)
v = Scalar(rand.u64())

# Native asset
asset = native_asset()
assert value_commit(rcv, v, asset) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + asset * v

assert value_commit(rcv, v) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + VALUE_COMMITMENT_VALUE_BASE * v
# Random non-native asset
asset = asset_id(randbytes(32), randbytes(512))
assert value_commit(rcv, v, asset) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + asset * v

if __name__ == '__main__':
test_value_commit()
4 changes: 2 additions & 2 deletions zcash_test_vectors/orchard/key_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@ def randbytes(l):
default_pk_d = fvk.default_pkd()

note_v = rand.u64()
note_type = None
asset = None
note_rho = Fp.random(rand)
note_rseed = rand.b(32)
note = OrchardNote(
default_d,
default_pk_d,
note_v,
note_type,
asset,
note_rho,
note_rseed,
)
Expand Down
32 changes: 16 additions & 16 deletions zcash_test_vectors/orchard/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
from ..utils import leos2bsp

class OrchardNote(object):
def __init__(self, d, pk_d, v, note_type, rho, rseed):
def __init__(self, d, pk_d, v, asset, rho, rseed):
assert isinstance(v, int)
self.d = d
self.pk_d = pk_d
self.v = v
self.note_type = note_type
self.asset = asset
self.rho = rho
self.rseed = rseed
self.rcm = self.rcm()
Expand All @@ -26,7 +26,7 @@ def __eq__(self, other):
self.d == other.d and
self.pk_d == other.pk_d and
self.v == other.v and
self.note_type == other.note_type and
self.asset == other.asset and
self.rho == other.rho and
self.rcm == other.rcm and
self.psi == other.psi
Expand All @@ -40,22 +40,22 @@ def psi(self):

def note_commitment(self):
g_d = diversify_hash(self.d)
note_type = self.note_type and leos2bsp(self.note_type)
return note_commit(self.rcm, leos2bsp(bytes(g_d)), leos2bsp(bytes(self.pk_d)), self.v, note_type, self.rho, self.psi)
asset = self.asset and leos2bsp(self.asset)
return note_commit(self.rcm, leos2bsp(bytes(g_d)), leos2bsp(bytes(self.pk_d)), self.v, asset, self.rho, self.psi)

def note_plaintext(self, memo):
return OrchardNotePlaintext(self.d, self.v, self.note_type, self.rseed, memo)
return OrchardNotePlaintext(self.d, self.v, self.asset, self.rseed, memo)

# https://zips.z.cash/protocol/nu5.pdf#notept
class OrchardNotePlaintext(object):
def __init__(self, d, v, note_type, rseed, memo):
self.leadbyte = bytes.fromhex('03' if note_type else '02')
def __init__(self, d, v, asset, rseed, memo):
self.leadbyte = bytes.fromhex('03' if asset else '02')
self.d = d
self.v = v
self.note_type = note_type
self.asset = asset
self.rseed = rseed
self.memo = memo
if note_type:
if asset:
assert(max(memo[512-32:]) == 0)

@staticmethod
Expand All @@ -72,7 +72,7 @@ def _from_bytes_orchard(buf):
return OrchardNotePlaintext(
buf[1:12], # d
struct.unpack('<Q', buf[12:20])[0], # v
None, # note_type
None, # asset
buf[20:52], # rseed
buf[52:564], # memo
)
Expand All @@ -82,13 +82,13 @@ def _from_bytes_zsa(buf):
return OrchardNotePlaintext(
buf[1:12], # d
struct.unpack('<Q', buf[12:20])[0], # v
buf[52:84], # note_type
buf[52:84], # asset
buf[20:52], # rseed
buf[84:564] + bytes(32), # memo
)

def __bytes__(self):
if self.note_type:
if self.asset:
return self._to_bytes_zsa()
else:
return self._to_bytes_orchard()
Expand All @@ -108,7 +108,7 @@ def _to_bytes_zsa(self):
self.d +
struct.pack('<Q', self.v) +
self.rseed +
self.note_type +
self.asset +
self.memo[:512-32]
)

Expand All @@ -119,11 +119,11 @@ def dummy_nullifier(self, rand):
d = fvk.default_d()

v = 0
note_type = None
asset = None

rseed = rand.b(32)
rho = Point.rand(rand).extract()

note = OrchardNote(d, pk_d, v, note_type, rho, rseed)
note = OrchardNote(d, pk_d, v, asset, rho, rseed)
cm = note.note_commitment()
return derive_nullifier(fvk.nk, rho, note.psi, cm)
26 changes: 15 additions & 11 deletions zcash_test_vectors/orchard/note_encryption.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/usr/bin/env python3
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
import sys;

from zcash_test_vectors.orchard.asset_id import native_asset

assert sys.version_info[0] >= 3, "Python 3 required."

from chacha20poly1305 import ChaCha20Poly1305
from hashlib import blake2b
Expand Down Expand Up @@ -133,7 +137,7 @@ def decrypt_using_ivk(self, ivk: Scalar, rho, cm_star):
return None

pk_d = OrchardKeyAgreement.derive_public(ivk, g_d)
note = OrchardNote(np.d, pk_d, np.v, np.note_type, rho, np.rseed)
note = OrchardNote(np.d, pk_d, np.v, np.asset, rho, np.rseed)

cm = note.note_commitment()
if cm is None:
Expand Down Expand Up @@ -170,7 +174,7 @@ def decrypt_using_ovk(self, ovk, rho, cv, cm_star):
if OrchardKeyAgreement.esk(np.rseed, rho) != esk:
return None
g_d = diversify_hash(np.d)
note = OrchardNote(np.d, pk_d, np.v, np.note_type, rho, np.rseed)
note = OrchardNote(np.d, pk_d, np.v, np.asset, rho, np.rseed)

cm = note.note_commitment()
if cm is None:
Expand Down Expand Up @@ -208,28 +212,28 @@ def randbytes(l):
g_d = diversify_hash(d)

is_native = i < 10
note_type = None if is_native else bytes(Point.rand(rand))

asset_point = native_asset() if is_native else Point.rand(rand)
asset_bytes_opt = None if is_native else bytes(asset_point)
rseed = rand.b(32)

memo = b'\xff' + rand.b(511)
if note_type:
if not is_native:
# Set the end of the memo to zeros
memo = memo[:512-32] + bytes(32)

np = OrchardNotePlaintext(
d,
rand.u64(),
note_type,
asset_bytes_opt,
rseed,
memo
)

rcv = rcv_trapdoor(rand)
cv = value_commit(rcv, Scalar(np.v))
cv = value_commit(rcv, Scalar(np.v), asset_point)

rho = np.dummy_nullifier(rand)
note = OrchardNote(d, pk_d, np.v, note_type, rho, rseed)
note = OrchardNote(d, pk_d, np.v, asset_bytes_opt, rho, rseed)
cm = note.note_commitment()

ne = OrchardNoteEncryption(rand)
Expand Down Expand Up @@ -268,7 +272,7 @@ def randbytes(l):
'ock': ne.ock,
'op': ne.op,
'c_out': transmitted_note_ciphertext.c_out,
'note_type': option(note_type),
'asset': asset_bytes_opt,
})

render_tv(
Expand All @@ -294,7 +298,7 @@ def randbytes(l):
('ock', '[u8; 32]'),
('op', '[u8; 64]'),
('c_out', '[u8; 80]'),
('note_type', 'Option<[u8; 32]>'),
('asset', 'Option<[u8; 32]>'),
),
test_vectors,
)
Expand Down