Merge pull request #28 from zcash-hackworks/orchard-commitments
Add Orchard commitments
This commit is contained in:
commit
12a07b4f61
2
ff1.py
2
ff1.py
|
@ -7,7 +7,7 @@ from binascii import unhexlify, hexlify
|
|||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
from sapling_utils import bebs2ip, i2bebsp, beos2ip, bebs2osp, cldiv
|
||||
from utils import bebs2ip, i2bebsp, beos2ip, bebs2osp, cldiv
|
||||
|
||||
# Morris Dworkin
|
||||
# NIST Special Publication 800-38G
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
|
||||
|
||||
from orchard_group_hash import group_hash
|
||||
from orchard_pallas import Fp, Scalar
|
||||
from orchard_sinsemilla import sinsemilla_hash_to_point
|
||||
from utils import i2lebsp
|
||||
|
||||
# Commitment schemes used in Orchard https://zips.z.cash/protocol/nu5.pdf#concretecommit
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#constants
|
||||
L_ORCHARD_BASE = 255
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit
|
||||
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 rcv_trapdoor(rand):
|
||||
return Scalar.random(rand)
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||
def sinsemilla_commit(r: Scalar, D, M):
|
||||
assert isinstance(r, Scalar)
|
||||
return sinsemilla_hash_to_point(D + b"-M", M).checked_incomplete_add(
|
||||
group_hash(D + b"-r", b"") * r
|
||||
)
|
||||
|
||||
def sinsemilla_short_commit(r: Scalar, D, M):
|
||||
return sinsemilla_commit(r, D, M).extract()
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit
|
||||
def note_commit(rcm, g_d, pk_d, v, rho, psi):
|
||||
return sinsemilla_commit(
|
||||
rcm,
|
||||
b"z.cash:Orchard-NoteCommit",
|
||||
g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s)
|
||||
)
|
||||
|
||||
def rcm_trapdoor(rand):
|
||||
return Scalar.random(rand)
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit
|
||||
def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp):
|
||||
return sinsemilla_short_commit(
|
||||
rivk,
|
||||
b"z.cash:Orchard-CommitIvk",
|
||||
i2lebsp(L_ORCHARD_BASE, ak.s) + i2lebsp(L_ORCHARD_BASE, nk.s)
|
||||
)
|
||||
|
||||
def rivk_trapdoor(rand):
|
||||
return Scalar.random(rand)
|
||||
|
||||
# Test consistency of ValueCommit^{Orchard} with precomputed generators
|
||||
def test_value_commit():
|
||||
from random import Random
|
||||
from tv_rand import Rand
|
||||
from orchard_generators import VALUE_COMMITMENT_RANDOMNESS_BASE, VALUE_COMMITMENT_VALUE_BASE
|
||||
|
||||
rng = Random(0xabad533d)
|
||||
def randbytes(l):
|
||||
ret = []
|
||||
while len(ret) < l:
|
||||
ret.append(rng.randrange(0, 256))
|
||||
return bytes(ret)
|
||||
rand = Rand(randbytes)
|
||||
|
||||
rcv = rcv_trapdoor(rand)
|
||||
v = Scalar(100000000)
|
||||
|
||||
assert value_commit(rcv, v) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + VALUE_COMMITMENT_VALUE_BASE * v
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_value_commit()
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
|
||||
|
||||
from pyblake2 import blake2s
|
||||
|
||||
from tv_output import render_args, render_tv
|
||||
from orchard_group_hash import group_hash
|
||||
from orchard_sinsemilla import sinsemilla_hash_to_point
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concretespendauthsig
|
||||
SPENDING_KEY_BASE = group_hash(b'z.cash:Orchard', b'G')
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
|
||||
NULLIFIER_K_BASE = group_hash(b'z.cash:Orchard', b'K')
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit
|
||||
VALUE_COMMITMENT_VALUE_BASE = group_hash(b'z.cash:Orchard-cv', b'v')
|
||||
VALUE_COMMITMENT_RANDOMNESS_BASE = group_hash(b'z.cash:Orchard-cv', b'r')
|
||||
|
||||
# Used in SinsemillaCommit (https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit)
|
||||
NOTE_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-NoteCommit-r', b'')
|
||||
NOTE_COMMITMENT_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-NoteCommit-M')
|
||||
|
||||
# Used in SinsemillaShortCommit (https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit)
|
||||
IVK_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-CommitIvk-r', b'')
|
||||
IVK_COMMITMENT_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-CommitIvk-M')
|
||||
|
||||
# Used in SinsemillaHash (https://zips.z.cash/protocol/nu5.pdf#orchardmerklecrh)
|
||||
MERKLE_CRH_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-MerkleCRH')
|
||||
|
||||
def main():
|
||||
render_tv(
|
||||
render_args(),
|
||||
'orchard_generators',
|
||||
(
|
||||
('skb', '[u8; 32]'),
|
||||
('nkb', '[u8; 32]'),
|
||||
('vcvb', '[u8; 32]'),
|
||||
('vcrb', '[u8; 32]'),
|
||||
('cmb', '[u8; 32]'),
|
||||
('cmq', '[u8; 32]'),
|
||||
('ivkb', '[u8; 32]'),
|
||||
('ivkq', '[u8; 32]'),
|
||||
('mcq', '[u8; 32]'),
|
||||
),
|
||||
{
|
||||
'skb': bytes(SPENDING_KEY_BASE),
|
||||
'nkb': bytes(NULLIFIER_K_BASE),
|
||||
'vcvb': bytes(VALUE_COMMITMENT_VALUE_BASE),
|
||||
'vcrb': bytes(VALUE_COMMITMENT_RANDOMNESS_BASE),
|
||||
'cmb': bytes(NOTE_COMMITMENT_BASE),
|
||||
'cmq': bytes(NOTE_COMMITMENT_Q),
|
||||
'ivkb': bytes(IVK_COMMITMENT_BASE),
|
||||
'ivkq': bytes(IVK_COMMITMENT_Q),
|
||||
'mcq': bytes(MERKLE_CRH_Q),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -8,7 +8,7 @@ import orchard_iso_pallas
|
|||
from pyblake2 import blake2b
|
||||
from orchard_pallas import Fp, p, q, PALLAS_B, Point
|
||||
from orchard_iso_pallas import PALLAS_ISO_B, PALLAS_ISO_A
|
||||
from sapling_utils import i2beosp, cldiv, beos2ip, i2leosp, lebs2ip
|
||||
from utils import i2beosp, cldiv, beos2ip, i2leosp, lebs2ip
|
||||
from tv_output import render_args, render_tv
|
||||
from tv_rand import Rand
|
||||
|
||||
|
@ -16,7 +16,9 @@ from tv_rand import Rand
|
|||
def sxor(s1,s2):
|
||||
return bytes([a ^ b for a,b in zip(s1,s2)])
|
||||
|
||||
def expand_message_xmd(msg, dst, len_in_bytes):
|
||||
def expand_message_xmd(msg: bytes, dst: bytes, len_in_bytes: int):
|
||||
assert isinstance(msg, bytes)
|
||||
assert isinstance(dst, bytes)
|
||||
assert len(dst) <= 255
|
||||
|
||||
b_in_bytes = 64 # hash function output size
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from orchard_group_hash import map_to_curve_simple_swu
|
||||
from orchard_iso_pallas import Point as IsoPoint
|
||||
from orchard_pallas import Fp
|
||||
from sapling_utils import leos2ip
|
||||
from utils import leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
from tv_rand import Rand
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from binascii import unhexlify
|
|||
from orchard_pallas import Fp
|
||||
from orchard_sinsemilla import sinsemilla_hash
|
||||
|
||||
from sapling_utils import i2lebsp, leos2bsp
|
||||
from utils import i2lebsp, leos2bsp
|
||||
|
||||
# https://zips.z.cash/protocol/nu5.pdf#constants
|
||||
MERKLE_DEPTH = 32
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
|
||||
|
||||
from sapling_jubjub import FieldElement
|
||||
from sapling_utils import leos2ip
|
||||
from utils import leos2ip
|
||||
|
||||
p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
|
||||
q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
|
||||
|
@ -31,6 +31,13 @@ class Fp(FieldElement):
|
|||
def from_bytes(buf):
|
||||
return Fp(leos2ip(buf), strict=True)
|
||||
|
||||
def random(rand):
|
||||
while True:
|
||||
try:
|
||||
return Fp(leos2ip(rand.b(32)), strict=True)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def __init__(self, s, strict=False):
|
||||
FieldElement.__init__(self, Fp, s, p, strict=strict)
|
||||
|
||||
|
@ -90,16 +97,29 @@ class Scalar(FieldElement):
|
|||
def __str__(self):
|
||||
return 'Scalar(%s)' % self.s
|
||||
|
||||
Fp.ZERO = Fp(0)
|
||||
Fp.ONE = Fp(1)
|
||||
Fp.MINUS_ONE = Fp(-1)
|
||||
@staticmethod
|
||||
def from_bytes(buf):
|
||||
return Scalar(leos2ip(buf), strict=True)
|
||||
|
||||
assert Fp.ZERO + Fp.ZERO == Fp.ZERO
|
||||
assert Fp.ZERO + Fp.ONE == Fp.ONE
|
||||
assert Fp.ONE + Fp.ZERO == Fp.ONE
|
||||
assert Fp.ZERO - Fp.ONE == Fp.MINUS_ONE
|
||||
assert Fp.ZERO * Fp.ONE == Fp.ZERO
|
||||
assert Fp.ONE * Fp.ZERO == Fp.ZERO
|
||||
def random(rand):
|
||||
while True:
|
||||
try:
|
||||
return Scalar(leos2ip(rand.b(32)), strict=True)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
for F in (Fp, Scalar):
|
||||
F.ZERO = F(0)
|
||||
F.ONE = F(1)
|
||||
F.MINUS_ONE = F(-1)
|
||||
|
||||
assert F.ZERO + F.ZERO == F.ZERO
|
||||
assert F.ZERO + F.ONE == F.ONE
|
||||
assert F.ONE + F.ZERO == F.ONE
|
||||
assert F.ZERO - F.ONE == F.MINUS_ONE
|
||||
assert F.ZERO * F.ONE == F.ZERO
|
||||
assert F.ONE * F.ZERO == F.ZERO
|
||||
|
||||
|
||||
#
|
||||
|
@ -209,6 +229,7 @@ class Point(object):
|
|||
return self.x
|
||||
|
||||
def __mul__(self, s):
|
||||
assert isinstance(s, Scalar)
|
||||
s = format(s.s, '0256b')
|
||||
ret = self.ZERO
|
||||
for c in s:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from orchard_pallas import Fp
|
||||
import numpy as np
|
||||
from sapling_utils import leos2ip
|
||||
from utils import leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
from tv_rand import Rand
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from orchard_pallas import Fp
|
||||
from orchard_poseidon import perm
|
||||
from sapling_utils import leos2ip
|
||||
from utils import leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
from tv_rand import Rand
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import math
|
|||
import orchard_iso_pallas
|
||||
|
||||
from orchard_pallas import Fp, Point
|
||||
from sapling_utils import cldiv, lebs2ip, i2leosp
|
||||
from utils import cldiv, lebs2ip, i2leosp
|
||||
from orchard_group_hash import group_hash
|
||||
from tv_output import render_args, render_tv
|
||||
from tv_rand import Rand
|
||||
|
|
|
@ -5,7 +5,7 @@ from pyblake2 import blake2s
|
|||
|
||||
from sapling_jubjub import Point, JUBJUB_COFACTOR
|
||||
from tv_output import render_args, render_tv
|
||||
from sapling_utils import i2leosp
|
||||
from utils import i2leosp
|
||||
|
||||
# First 64 bytes of the BLAKE2s input during group hash.
|
||||
# This is chosen to be some random string that we couldn't have
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
|
||||
|
||||
from sapling_utils import i2lebsp, leos2ip, i2leosp
|
||||
from utils import i2lebsp, leos2ip, i2leosp
|
||||
|
||||
q_j = 52435875175126190479447740508185965837690552500527637822603658699938581184513
|
||||
r_j = 6554484396890773809930967563523245729705921265872317281365359162392183254199
|
||||
|
|
|
@ -7,7 +7,7 @@ from sapling_generators import PROVING_KEY_BASE, SPENDING_KEY_BASE, group_hash
|
|||
from sapling_jubjub import Fr
|
||||
from sapling_merkle_tree import MERKLE_DEPTH
|
||||
from sapling_notes import note_commit, note_nullifier
|
||||
from sapling_utils import leos2bsp, leos2ip
|
||||
from utils import leos2bsp, leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
|
||||
#
|
||||
|
|
|
@ -4,7 +4,7 @@ import sys; assert sys.version_info[0] >= 3, "Python 3 required."
|
|||
from binascii import unhexlify
|
||||
|
||||
from sapling_pedersen import pedersen_hash
|
||||
from sapling_utils import i2lebsp, leos2bsp
|
||||
from utils import i2lebsp, leos2bsp
|
||||
|
||||
MERKLE_DEPTH = 32
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from sapling_generators import VALUE_COMMITMENT_VALUE_BASE, VALUE_COMMITMENT_RAN
|
|||
from sapling_jubjub import Fr, JUBJUB_COFACTOR
|
||||
from sapling_key_components import SpendingKey, diversify_hash
|
||||
from sapling_notes import note_commit
|
||||
from sapling_utils import leos2bsp, leos2ip
|
||||
from utils import leos2bsp, leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from sapling_pedersen import (
|
|||
mixing_pedersen_hash,
|
||||
windowed_pedersen_commitment,
|
||||
)
|
||||
from sapling_utils import i2lebsp
|
||||
from utils import i2lebsp
|
||||
|
||||
def note_commit(rcm, g_d, pk_d, v):
|
||||
return windowed_pedersen_commitment(rcm, [1] * 6 + i2lebsp(64, v) + g_d + pk_d)
|
||||
|
|
|
@ -7,7 +7,7 @@ from sapling_generators import (
|
|||
WINDOWED_PEDERSEN_RANDOMNESS_BASE,
|
||||
)
|
||||
from sapling_jubjub import Fr, Point
|
||||
from sapling_utils import cldiv, i2leosp
|
||||
from utils import cldiv, i2leosp
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -7,7 +7,7 @@ from pyblake2 import blake2b
|
|||
from sapling_generators import SPENDING_KEY_BASE
|
||||
from sapling_jubjub import Fr, Point, r_j
|
||||
from sapling_key_components import to_scalar
|
||||
from sapling_utils import cldiv, leos2ip
|
||||
from utils import cldiv, leos2ip
|
||||
from tv_output import render_args, render_tv
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from pyblake2 import blake2b
|
|||
|
||||
from sapling_key_components import to_scalar, prf_expand, diversify_hash, DerivedAkNk, DerivedIvk
|
||||
from sapling_generators import SPENDING_KEY_BASE, PROVING_KEY_BASE
|
||||
from sapling_utils import i2leosp, i2lebsp, lebs2osp
|
||||
from utils import i2leosp, i2lebsp, lebs2osp
|
||||
from ff1 import ff1_aes256_encrypt
|
||||
from tv_output import render_args, render_tv, option, Some
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import struct
|
|||
|
||||
from sapling_generators import find_group_hash, SPENDING_KEY_BASE
|
||||
from sapling_jubjub import Fq, Point
|
||||
from sapling_utils import leos2ip
|
||||
from utils import leos2ip
|
||||
from zc_utils import write_compact_size
|
||||
|
||||
MAX_MONEY = 21000000 * 100000000
|
||||
|
|
Loading…
Reference in New Issue